Technically, the correct name for this two-wire serial bus is Inter-IC bus or I2C, though it is sometimes written as I2C. I2C uses two pins:

  • SCL – Serial Clock Line

  • SDA – Serial Data line (bidirectional)

Because both the value and direction (input versus output) of the SCL and SDA pins must be rapidly and precisely controlled, dedicated I2C support functions have been added to SNAPpy. Using the following functions, your SNAPpy script can operate as an I2C bus master and can interact with I2C slave devices:

To allow these functions to be as fast as possible, the IO pins used for I2C SDA and SCL are fixed. The IO pins that are reserved for I2C varies by platform, so refer to SNAP RF Modules. These pins are not dedicated for I2C, so if you are not using the I2C functions, you may use the reserved pins for other functions.

Unlike CBUS and SPI, I2C does not use separate Chip Select pins. The initial data bytes of each I2C transaction specify an I2C address. Only the addressed device will respond, so no additional GPIO pins are needed.

The specifics of which bytes to send to a given I2C slave device (and what the response will look like) depend on the I2C device itself. You will have to refer to the manufacturer’s data sheet for any given device to which you wish to interface.

I2C Restart

Usually, I2C read and write commands begin with a hardware handshake sequence referred to as an “I2C Start” and end with a mirror-image hardware handshake sequence referred to as an “I2C Stop”. The original implementations of i2cRead() and i2cWrite() automatically generate these handshake sequences for you.

A typical use case looks something like this:

bytes_to_write   = <sets up for the next i2cRead()>
addressing_bytes = <address that begins the actual read>

i2cWrite(bytes_to_write, retries, ignoreFirstAck)
info = i2cRead(addressing_bytes, retries, ignoreFirstAck)

This use case works with a wide variety of I2C chips. However, various manufacturers have introduced new parts that won’t work with the above code snippet. This is because the “I2C Stop” sequence generated at the end of the i2cWrite() function resets the I2C chip’s internal address registers, thus undoing the setup that was accomplished by writing bytes_to_write. Instead of an “I2C Start, I2C Stop, I2C Start, I2C Stop” sequence, these devices require an “I2C Start, I2C Restart, I2C Stop” sequence.


To determine if your particular I2C device requires an “I2C Restart”, refer to the manufacturer’s datasheet for the I2C part. Some manufacturer data sheets refer to the “I2C Restart” as a “Repeated Start.”

Beginning in SNAP 2.5, an optional endWithRestart argument was introduced to the i2cWrite() function which allows you to interact with these devices natively. Just change the above example to include the new parameter:

i2cWrite(bytes_to_write, retries, ignoreFirstAck, True)   # Passing True for endWithRestart
info = i2cRead(addressing_bytes, retries, ignoreFirstAck)

Prior to SNAP 2.5, the only way to interface to devices having this “I2C Restart” requirement was to implement a custom version of the i2cWrite() functionality in your own SNAPpy script, using the lower-level SNAPpy setPinDir(), readPin(), and writePin() functions.


For examples of using the new SNAPpy I2C functions to interface to external devices, see the following scripts that are bundled with Portal:

  • i2cTests.py – This is the overall I2C demo script

  • synapse.M41T81.py – Example of interfacing to a clock/calendar chip

  • synapse.CAT24C128.py – Example of interfacing to an external EEPROM

  • synapse.M41T00CAP.py - Example of interfacing to a real-time clock chip

Script i2cTests.py imports two of the other scripts and exercises some of the functions within them.

See also