One of the toughest challenges faced in today’s evolving market is communicating with legacy I2C and serial devices. To satisfy this need, NetBurner has added I2C-to-Ethernet support to the SB70LC’s serial to Ethernet factory application.
For those new to NetBurner, we provide a single source of hardware, software, development tools and technical support for all your network-enabled embedded system needs. NetBurner is dedicated to supporting the product and service needs of our customers.
In response to customer requests, NetBurner has added network-enabled I2C support to the out-of-the-box application of the SB70LC. The new version of the SB70LC’s factory application has all of the functionality of the previous application – serial tunneling through SSL and SSH protected Ethernet – but now has additional I2C features. Using the application’s webpage, you can configure the settings for I2C-to-Ethernet communication. You can access the application’s webpage by simply inputting the device’s IP address into your web browser. On the “TCP” page, you can configure several TCP settings for I2C-to-Ethernet communication. These options include enabling listening for incoming TCP connections, choosing the port to listen on, adjusting the inactivity timeout, adjusting the idle time before allowing new connections, and the ability to use SSL instead of TCP for improved security. To configure I2C settings, a dedicated “I2C” page has been added. This page gives you the option to adjust the I2C bus speed, change the master I2C address for your SB70LC, save the I2C address in non-volatile memory, and scan the I2C bus for connected devices. After configuring settings through the webpage, you can begin communicating from I2C-to-Ethernet by establishing a TCP or SSL connection. This can be done using a virtual terminal connection such as Telnet or OpenSSL. Once a connection is established you can use NetBurner’s custom protocol to communicate from I2C-to-Ethernet. With these new features, you can communicate with your I2C devices anywhere a TCP or SSL connection can be established.
This application can be used to extend the functionality of your embedded project, just like in this example where we use an MP23017 I2C I/O expander to add 8 network-enabled GPIOs to an SB70LC. For the sake of simplicity, we demonstrate the additional GPIOs using an array of LEDs.
What should you do if you need access to more GPIO pins than are available on your microcontroller? This is a situation where I/O port expanders come in handy. The MP23017 converts data sent to it by the I2C communication bus into 16 I/O pins. Since the MP23017 can be configured to have eight unique addresses, eight of these I/O expanders can be used on the same I2C bus to obtain an additional 128 I/O pins.
Now that we know the value of the I/O port expander, how do we get this thing set up? You’ll need a NetBurner SB70LC, an MP23017 I/O expander, 16 LEDs, and 16 90-Ohm resistors. Begin by setting up your SB70LC as you normally would and construct your circuit according to the following schematic. Refer to the datasheet for the SB70LC and the MCP23017 for pinouts.
The script to communicate with the I/O port expander was written in Python. It does this by establishing a Telnet connection, opening a predefined file that contains a list of commands according to NetBurner’s I2C-to-Ethernet protocol, and sends the file line by line through the Telnet client.
import sys import argparse import time import telnetlib TIMEOUT = 2.5 # Parse arguments parser = argparse.ArgumentParser() parser.add_argument("cmd_file", help="File containing one I2C-to-Ethernet Protocol command per line") parser.add_argument("host", help="IP address to listen on") parser.add_argument("port", type=int, help="Port to listen on") parser.add_argument("delay", type=float, help="Delay in seconds between each line of commands") args = parser.parse_args() # Open a Telnet connection tn = telnetlib.Telnet(args.host, args.port) # Open file that contains I2C-to-ethernet commands f = open(args.cmd_file, 'rb') # Read a line from args.file, print the line, write the line to Telnet, print the line # received from Telnet. Loop until all lines in args.file have been read for line in f: print(b'Tx: ' + line ), tn.write( line ), rx_line = b'Rx: ' + tn.read_until(b'.', TIMEOUT) #read until "." is found or a timeout has occurred print(rx_line) time.sleep(args.delay) # Close opened files f.close() tn.close()
Below is an example file containing a list of I2C-to-Ethernet commands. In order to understand these commands, some basic knowledge of the I2C communication protocol is required. If you are unfamiliar with the I2C protocol, refer to the Wikipedia page here before reading any further. It might also be handy to have the datasheet for the MCP23017 open as a reference for configuring the port expander.
#WW2003000000 #WW200312FFFF #WW2003120000 #WW200312FFFF #WW2003120000 #WW2003128001 #WW2003124002 #WW2003122004 #WW2003121008 #WW2003120810 #WW2003120420 #WW2003120240 #WW2003120180 #WW2003120240 #WW2003120420 #WW2003120810 #WW2003121008 #WW2003122004 #WW2003124002 #WW2003128001 #WW2003120000 #WW200312FFFF #WW2003120000 #WW200312FFFF #WW2003120000
Configuring the I/O Port Expander
The first command in the list configures the I/O pins of the MCP23017 to be outputs. This allows us to source power from the I/O pins to light up the LEDs. We can do this by writing to the IODIRA and the IODIRB registers of the MCP23017, which control the operation of the I/O pins. Both registers are one byte in size with each bit representing an I/O pin. IODIRA corresponds to pins 21 through 28 and IODIRB corresponds to pins 1 through 8. For example, if we write 0x01 to IODIRB, then pin 1 of the MCP23017 will be an input and pins 2 through 8 will be outputs. In this example, we want to drive 16 LEDs so we will write 0x00 to both registers. In order to write zeroes to these two registers, we must first address the registers. According to table 1–4 of the MCP23017 datasheet, the address of IODIRA is 0x00 and the address of IODIRB is 0x01. Luckily, IODIRB is the next address after IODIRA. This means that after we address and write a byte to IODIRA, we can simply write a byte to IODIRB without addressing the register.
The whole process will look like the following: address the I/O port expander using its I2C address, address the IODIRA register, write a byte (that is addressed to IODIRA), and continue to write a second byte (that will be addressed to IODIRB automatically). The following is the command according to NetBurner’s I2C-to-Ethernet protocol:
Let’s take this step by step. The “#WW” is the command to write a buffer to an I2C address. All of the following numbers must be read in pairs because they represent a byte in hex. The next byte, 0x20, represents the I2C address of the I/O port expander. Following the I2C address is 0x03, which represents the size of the buffer in bytes. Proceeding the length of the buffer, is the address of the IODIRA register – which just so happens to be 0x00. Lastly, we write two bytes of 0x00: one that will be written to the IODIRA register, and a second to the IODIRB register. After running this command, all 16 I/O pins of the I/O port expander will be configured as outputs.
Setting the I/O Pins
Now that the I/O are configured, we can write the I2C commands to the I/O port expander that will light up the LEDs. This is done by writing to the GPIOA and GPIOB register of the port expander. These registers are also one byte in size. In order to set a particular I/O pin HIGH, which will light up an LED, we must write a 1 to the corresponding bit of the GPIO register. For example, if we want to turn on the LED connected to pin 1, we will write 0x01 to register GPIOB. This will light up only the LED connected to pin 1 and set pins 2 through 8 to a digital LOW value.
The process of writing to the GPIO registers looks similar to the configuration process that was explained previously. From table 1–4 of the datasheet, we can find that the address of GPIOA is 0x12 and GPIOB is 0x13. Luckily, the address of GPIOB follows after the address of GPIOA. This means that after addressing and writing to GPIOA, we can simply write another byte to GPIOB without having to address it. The process of writing to the GPIO registers goes like this: first we address the MCP23017 using its I2C address, followed by addressing the GPIOA register, then we write a byte to the GPIOA register, and finally we write a byte to the GPIOB register (since GPIOB is addressed automatically). The actual implementation to light up all 16 LEDs looks like this:
Again, the “#WW” is the command to write a buffer to an I2C address, and the following 0x20 is the I2C address of the I/O port expander. The proceeding byte, 0x03, is the length of the buffer that is to be written. After the size of the buffer, we write 0x12 to the I/O port expander which is the address of the GPIOA register. Now that the GPIOA register is addressed, we then write 0xFF to the GPIOA register (to set pins 21 through 28 HIGH). Finally, we write 0xFF to the GPIOB register (to set pins 1 through 8 HIGH). This line of code lights up all 16 LEDs.
Running the Script
Now that you have a rough understanding of the I2C-to-Ethernet protocol, we can run the Python script to start the LED sequence. Open a terminal window and navigate to the directory containing scan_leds.py and led_commands.txt. Run the script using the following command:
python scan_leds.py led_commands.txt <IP address> <port> <delay>
For help with the arguments, use the following command:
python scan_leds.py -h
By running the script, the list of commands will run through an animation that begins by flashing all of the LEDs, then scans through the LEDs, and finishes by blinking the LEDs again.
For more information on the SB70LC, check out the documentation here. If you would like to learn more about the I2C-to-Ethernet features, visit the “Help” page of the application web page.