JetsonHacks

Developing on NVIDIA® Jetson™ for AI on the Edge

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.

Facebook
Twitter
LinkedIn
Reddit
Email
Print

58 Responses

  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!

    1. 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!

      1. 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

        1. 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.

          1. 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!

            1. 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

              1. 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?

    1. How did you wire it? What command line did you use for i2cdetect? If it is not found, it is an issue with wiring or the command sequence to discover it.

      1. Me too. the response is:
        “`
        $ i2cdetect -y -r 0
        0 1 2 3 4 5 6 7 8 9 a b c d e f
        00: — — — — — — — — — — — — —
        10: — — — — — — — — — — — — — — — —
        20: — — — — — — — — — — — — — — — —
        30: — — — — — — — — — — — — — — — —
        40: — — — — — — — — — — — — — — — —
        50: — — — — — — — — — — — — — — — —
        60: — — — — — — — — — — — — — — — —
        70: — — — — — — — —
        “`
        and
        “`
        $ i2cdetect -l
        i2c-3 i2c 7000c700.i2c I2C adapter
        i2c-1 i2c 7000c400.i2c I2C adapter
        i2c-6 i2c Tegra I2C adapter I2C adapter
        i2c-4 i2c 7000d000.i2c I2C adapter
        i2c-2 i2c 7000c500.i2c I2C adapter
        i2c-0 i2c 7000c000.i2c I2C adapter
        i2c-5 i2c 7000d100.i2c I2C adapter
        “`
        Should Tugra on bus0? May I say on my Nano, the pin27,28 I2C is on bus6?

      1. 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.??

        1. 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?

    1. 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.

  6. Great!
    I got a little trouble, when try to use GPIO:
    gpio.setmode(gpio.BOARD)
    i get an error:
    File “/usr/lib/python3/dist-packages/Jetson/GPIO/gpio.py”, line 298, in setmode
    ValueError: A different mode has already been set!
    Can you explain, how can i use other pins to control (BL-TB6550-V2.0 stepper driver direction)?
    For example: gpio.output(direction_L_pin,gpio.HIGH)

    1. You are welcome. I don’t know what “link it with ROS” means. Typically you write a ROS node to interface with a specific device, not a communications pathway. Thanks for reading!

  7. Mr. Kangalow, do you know what the maximum current the 3.3V (GPIO pin 2) can source? I am aware the 5V pin is a direct connection to the DC supply for the Nano but they must have a voltage regulator to provide the 3.3V….
    Thanks.

  8. Everything seems to be conncected right but for some reason my servos just won’t move. I thought they were broken and even used some new ones I had. I have to idea why they wont turn. When I run sudo i2cdetect -y -r 1 it shows that everything is connected so im not sure whats wrong.

  9. Can the motor driver control 2 BLDC motors with 24-36v independently ?
    I am trying to do differential steering.

  10. Mr. Kangalow can u please help with in interfacing mlx90614 sensor with jetson nano. After connecting when using sudo i2cdetect -r -y 1, it is not detecting anything.

    1. There is not enough information here to offer any advice. Typically people provide information on how a particular device is wired to the Jetson, the type of board the device is on. Did you wire a chip directly to the GPIO pins?

  11. Hello from France !

    I come from Arduino and i’m trying to learn how to use my nano 2GB 🙂
    I should say that your courses are VERY appreciated !

    But, with this topic of controling a servo via I2C using the adafruit board, i’m facing a problem: when i write a nangle, the actual rotation of the servo’s shaft doesn’t match exactly the asked angle…

    (I’m using a “standard blue 9G hobby servo” very similar to yours)

    for exemple:
    – I ask for 0 degree angle
    – the shaft rotate
    – then i put the plastic arm on the shaft to have a kind of a reference position
    – I ask for a 90 degree angle
    then i mesure (by eye) the arm position and it is not at 90… (my guess is about 60)
    same goes for 180 (it looks like 120)

    Does anybody have a clue for me ??

    I’m using a nano 2 GB, LXDE 18.04, and python 3.6.9

    Thank your for your help
    bye

  12. Thanks for your suggestion Kangalow,
    I’m gonna watch it.
    I’m ALSO a huge fan of Paul 😉
    bye and thanks for your help

  13. im using ros melodic on the jetson nano, i think it is python 2. servokit is python 3. how do you get servokit to work in ros melodic?

  14. Thanks author for sharing this amazing tutorial! I have a Xavier NX and I plan to run on it. What modifications/changes would you recommend us to make this tutorial work for Xavier as well? Thank you!

  15. Which version of python are you using for this? I’m having trouble with the adafruit_servokit module when running 3.6 and I get the following message. It works fine when I upgraded to 3.7, but I had trouble install other modules running 3.7. I might be missing some library in 3.6, do you have some insight or solution?

    Python 3.6.9 (default, Dec 8 2021, 21:08:43)
    [GCC 8.4.0] on linux
    Type “help”, “copyright”, “credits” or “license” for more information.
    >>> from adafruit_servokit import ServoKit
    Traceback (most recent call last):
    File “”, line 1, in
    File “/usr/local/lib/python3.6/dist-packages/adafruit_servokit.py”, line 35, in
    import board
    File “/usr/local/lib/python3.6/dist-packages/board.py”, line 39, in
    import adafruit_platformdetect.constants.boards as ap_board
    File “/usr/local/lib/python3.6/dist-packages/adafruit_platformdetect/__init__.py”, line 10, in
    from .board import Board
    File “/usr/local/lib/python3.6/dist-packages/adafruit_platformdetect/board.py”, line 24
    from __future__ import annotations
    ^
    SyntaxError: future feature annotations is not defined

    1. I don’t have any real insight. The issue that you are encountering is likely that the servo kit library has changed/updated since this post. I would try reverting to a version of the servo kit library that was contemporary with the article first.

    2. I understand that if I want to use PCA9685 in Jetson Nano with Adafruit driver I have to install Python 3.7 , but this version produce issues. So my question is , if this issue has no workaround do you know in what way I can use PCA9685 ?

  16. Hi. I’m trying to use two PCA9685 boards with a Raspberry pi 3B+. I’ve physically changed the i2c address on one board to 0x41 but I can’t get ServoKit to respond to this second board. I guess the library for ServoKit only looks for 0x40 but I don’t know how to ask it nicely to recognise both boards. I’ve looked in i2c_device.py but a bit lost. Help?

  17. They are piggy backed to the same I2c connections as per adafruit instructions. That is why you need to change the board address

  18. I have been hitting a number of errors following the instructions. First the installServoKit.sh script was failing because the Blinka dependency of >7.0 couldn’t be met. I worked around this by installing python3.7 and creating a virtual environment.

    Then the /opt/nvidia/jetson-gpio/etc/99-gpio.rules files seems to no longer be in present in the jetson as the jetson-gpio subdirectory isn’t present on my device. I wrote my own based on what I could find on the web.

    But then after reboot I am still access denied. I tried both the script, as well as adding my account directly – even setting ‘r’ permissions to all users, and still I am getting the error below.

    Interestingly I2Cdetect is showing the device on pin 40 just fine…

    File “/home/jason/ServoKit/MyEnv/lib/python3.7/site-packages/Jetson/GPIO/gpio.py”, line 33, in
    raise RuntimeError(“The current user does not have permissions set to access the library functionalites. Please configure permissions or use the root user to run this. It is also possible that {} does not exist. Please check if that file is present.”.format(_GPIOCHIP_ROOT))
    RuntimeError: The current user does not have permissions set to access the library functionalites. Please configure permissions or use the root user to run this. It is also possible that /dev/gpiochip0 does not exist. Please check if that file is present.

    Pulling my hair out – not that I have much left!

Leave a Reply

Your email address will not be published. Required fields are marked *

Disclaimer

Some links here are affiliate links. If you purchase through these links I will receive a small commission at no additional cost to you. As an Amazon Associate, I earn from qualifying purchases.

Books, Ideas & Other Curiosities