Jetson Nano – Using I2C

A simple way to control Servo Motors on the NVIDIA Jetson Nano Developer Kit is to connect the Jetson Nano via I2C to a PWM Driver. Looks here:

Jetson Nano I2C

Background

There are four types of serial communication ports on the Jetson Nano. The most obvious is USB, which is where you plug in the mouse and keyboard. The other three are hidden away in the headers on the board.

We’ve talked about Universal Asynchronous Receiver-Transmitter (UARTs) before (one of which is the serial console). The Jetson Nano has two, one on the J44 Serial Port header, and one on the J41 Expansion Header.

There are also provisions for Serial Peripheral Interface (SPI) ports. In the default Nano Image configuration, the Jetson Nano does not have SPI port access. However, the device tree can be reconfigured for accessing SPI through the J41 Expansion Header. In the Jetson Nano J41 Pinout, NVIDIA recommends placement for two SPI ports.

Note: You may also hear the J41 Expansion Header referred to as the GPIO Header. The two terms are interchangeable.

The remaining serial ports are Inter-integrated Circuit (I2C). The Inter-integrated Circuit (I2C) Protocol goes by various names depending on who you talk to, “I squared C”, “I two C” or “I I C”. I2C is intended for short distance communication within a single device. One of the reasons that it is popular with the maker crowd is that it only requires two wires (the “data” and the “clock”) along with power and ground to get functioning.

In order to control Servo Motors, we use Pulse Width Modulation (PWM). The Jetson Nano has the capability of generating two PWM pulses on the J41 Header. However, you need to reconfigure the device tree to make these signals available on Pins 32 and 33. A simpler way is to use an external piece of hardware.

For our demo, we use a PCA9685 12-bit PWM/Servo Driver. Using only two pins, we can control 16 free-running PWM outputs. You can even chain up 62 breakouts to control up to 992 PWM outputs! The PCA9685 breakout board is a nice little application specific micro controller for servo control.

Parts

In the video, we use several different pieces of hardware. Here’s a partial list:

For the demo, we also use a WiFi card and game controller. Here’s an article about how to install those.

Software Install

Here we will be using the Adafruit ServoKit Library. This is a Python library built on top of the Jetson.GPIO library. There are several I2C libraries available. For example, if you are using C, C++ or Python you can use libi2c for low level access to I2C.

On the JetsonHacksNano account on Github, there is a ServoKit repository. Clone the repository, switch to that repositories directory and install the adafruit-circuitpython-servokit library:

$ git clone https://github.com/JetsonHacksNano/ServoKit
$ cd ServoKit
$ ./installServoKit.sh

This installs the ServoKit library and also sets up the I2C and GPIO permissions so that we can run programs from user space. GPIO permissions are added to support the underlying Jetson.GPIO library.

Note: The group changes do not take effect until login. You must logoff/login or reboot the computer for the changes to take effect.

Demo 1

In the default Jetson Nano Image, there are two I2C ports available on the J41 Header. From the Jetson Nano J41 Pinout :

  • I2C Bus 1 SDA is on Pin 3
  • I2C Bus 1 SCL is on Pin 5
  • I2C Bus 0 SDA is on Pin 27
  • I2C Bus 0 SCL is on Pin 28

Note: Before wiring the Jetson, make sure that the power is disconnected. When the power is plugged in, the power and ground rails on the headers are always live, even if the processor itself is off.

For the first demo in the video, we wire Bus 1:

  • J41 Pin 3 (SDA) -> PCA9685 SDA
  • J41 Pin 5 (SCL) -> PCA9685 SCL
  • J41 Pin 1 (3.3V) -> PCA9685 VCC
  • J41 Pin 6 (GND) -> PCA9685 GND

A 5V 4A power supply is connected to the PCA 9685. The SG90 micro server is connected to port 0 of the PWM outputs. Note that the GND signal is towards the outside edge of the board, the control signal is towards the center of the board.

After wiring the board, plug the Jetson in. Once the Nano is up and running, open a terminal and execute:

$ i2cdetect -y -r 1

The default address of the PCA9685 is 0x40 (this is hexadecimal 40). You should see an entry of ’40’ in the addresses listed. If you do not see the entry, then the wiring is probably incorrect. When the address does not show up, then you will not be able to use the device. Note: You can change the default address of the PCA9685, so you will need to take that into account when you check the device visibility.

We are now ready to run our first demo:

$ python3
>>> from adafruit_servokit import ServoKit
>>> kit = ServoKit(channels=16)
>>> kit.servo[0].angle=137
>>> kit.servo[0].angle=25
>>> quit()

Our servo should move to the appropriate angle when we command it.

Demo 2

Demo 2 is a little more involved. After powering down and unplugging the Nano, we wire up the PCA9685 and add the pan and tilt servo connections:

  • J41 Pin 27 (SDA) -> PCA9685 SDA
  • J41 Pin 28 (SCL) -> PCA9685 SCL
  • J41 Pin 1 (3.3V) -> PCA9685 VCC
  • J41 Pin 6 (GND) -> PCA9685 GND

Here’s the full wiring diagram:

Dual Servo Wiring

J41 Pins 27 and 28 on the Jetson Nano conenct to I2C bus 0. Check to make sure that we can see 0x40 on IC2 Bus 0:

$ i2cdetect -y -r 0

Make sure that the game controller is paired to the Jetson Nano as described in the Jetson Nano Wifi Article (also shown in the video). You can run jstest-gtk to make sure everything is happy.

Then you are ready to run the demo:

$ python3 servoPlay.sh

After initialization (this takes a few seconds), you should be able to control the pan and tilt servos with the game controller. The left joystick in the X direction controls the bottom servo. The right joystick in the Y directions controls the top servo.

You should look through the servoPlay.sh script for insight on how all of this fits together. Also, we demonstrate the ServoKit ‘throttle’ command, which is useful for controlling continuous servos and the similar electronic speed controllers for motors.

Conclusion

This article is more complicated than the actual code and wiring itself. Get ahold of the parts, wire them up, and go play!

Notes

The Jetson Nano is running L4T 32.1 with no modifications.

25 Comments

  1. Thanks again for posting this. You saved us so much trouble, we now have plenty of extra time for contemplating the universe beyond our benchtop. This has been a wonderful gift!

    • Thank you for the kind words. Hopefully y’all can spend time doing interesting things rather than this silly hardware bit-banging just to get motors turning. Thanks for reading!

      • Really your stuff is amazing!

        On the other hand I just got my parts from Amazon, I downloaded the repo, installed as instructuted (No failures or complains) but when I tried to run the python3 servoPlay.py it showed the following “Board not supported” error. (Sorry I’m totally new on Python and on adafruit)

        $ python3 servoPlay.py
        Traceback (most recent call last):
        File “servoPlay.py”, line 6, in
        from adafruit_servokit import ServoKit
        File “/usr/local/lib/python3.6/dist-packages/adafruit_servokit.py”, line 52, in
        import board
        File “/usr/local/lib/python3.6/dist-packages/board.py”, line 98, in
        raise NotImplementedError(“Board not supported”)
        NotImplementedError: Board not supported

        • What L4T release are you using? If it is L4T 32.2, then there appears to be an issue with how the Nano is identified. The Nano is identified in the Jetson.GPIO library from the file /proc/device-tree/compatible and looks for:

          nvidia,jetson-nano

          That was correct for earlier releases, but the file now reads:

          nvidia,jetson-nanonvidia,tegra210

          Which makes it look like someone forgot to add a line break. You can fix the Jetson.GPIO library if need be.

          • Yes. I’m using the latest and “greatest” L3T 32.2

            I will find the way to fix the jetson.GPIO library (I will find the way, I’m new on this but very interested to learn this stuff…)

            Thanks for your help!

            • Hey,
              There’s definitely an issue with the last version of Jetson install but the issue seems to be in the /proc/device-tree/model file.

              The file used to read: “jetson nano”
              and now it is : “NVIDIA Jetson Nano Developer Kit”

              The adafruit detector is looking for “nano” without the capital N, so the board is not recognized.

              I opened a pull request so it should be fixed soon :
              https://github.com/adafruit/Adafruit_Python_PlatformDetect/pull/34

              • Wow, they’re quick over there at Adafruit. They’ve already merged your pull request into master. Thank you!
                I’ll point out that there’s also an issue with the Jetson.GPIO library with the same issue.

  2. Thanks for all the great content! Its been super helpful

    I am trying to use an adafruit MCP4725 DAC with the I2C on the Jetson Nano. I have installed blinka and the adafruit-circuitpython-mcp4725

    I am using I2C 1 on pins 3 & 5. I am able to see the device on address 0x60.

    When I run the following code, I keep getting a write error. I am using sudo.
    import board
    import busio
    import adafruit_mcp4725
    i2c = busio.I2C(board.SCL, board.SDA)
    dac = adafruit_mcp4725.MCP4725(i2c)
    dac.value = 32767

    Traceback (most recent call last):
    File “”, line 1, in
    File “/usr/local/lib/python3.6/dist-packages/adafruit_mcp4725.py”, line 131, in value
    self._write_fast_mode(raw_value)
    File “/usr/local/lib/python3.6/dist-packages/adafruit_mcp4725.py”, line 90, in _write_fast_mode
    self._i2c.writeto(self._address, self._BUFFER, end=2)
    File “/home/vick/.local/lib/python3.6/site-packages/busio.py”, line 64, in writeto
    return self._i2c.writeto(address, memoryview(buffer)[start:end], stop=stop)
    File “/home/vick/.local/lib/python3.6/site-packages/adafruit_blinka/microcontroller/generic_linux/i2c.py”, line 38, in writeto
    self._i2c_bus.write_bytes(address, buffer[start:end])
    File “/home/vick/.local/lib/python3.6/site-packages/Adafruit_PureIO/smbus.py”, line 244, in write_bytes
    self._device.write(buf)
    OSError: [Errno 121] Remote I/O error

    What am I doing wrong ?

    Thanks
    Vick

  3. Hi , where i can learn to use python (or c++ would be nice) for jetson nano. For exemple i want to comunicate with an esp32 and i also want to save some data to memory, and also to predict somethin using ai with jetson nano

  4. I connected the 9685 breakout board to the nano and tried running i2cdetect… It does not find any devices (I have a servo on header 0 ). Any suggestions for troubleshooting?

      • yes, exactly. I am trying to control two 6v dc motors and i am having L298N motor driver. I was wondering which is the best PWM channels or driver which support jetson Nano PWM pins(like adafruit) to control them.??

        • Typically if you are using more than one PWM device, you would use some type of external PWM driver (as discussed above) or micro controller (such as an Arduino). Like the Raspberry Pi, there is only one PWM capable pin on the Jetson. Also, SBCs like the RPi and Jetson usually have a lot of overhead in running a real time device like that as their operating systems are not really designed for real time control.

  5. My servos don’t work, i2cdetect is correct, the connections are correct, but when I run kit.servo [0] .angle = 80 it doesn’t move

    Any ideas?

    • From your description it is difficult to tell what issue you are having. Typically you would check the log to see if there were any exceptions. If you want others to help, you would state which version of L4T you are using, the command that you used to run the program, the actual program itself and so on.

Leave a Reply

Your email address will not be published.


*