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:
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:
- Jetson Nano
- PCA9685 Breakout Board (Adafruit)
- SG90 Micro Servo
- Alternate inexpensive Adafruit Mini Pan and Tilt with Servos
- As shown in the video, the Robot Geek Pan and Tilt Kit w/ Servos is available from Trossen Robotics
- Breadboard
- Hookup wire
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:

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.
58 Responses
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!
Live you site, work and videos! Thanks you so much!
Thank you for the kind words, and 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.
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
I don’t have any idea why it doesn’t work, it is difficult to debug without the actual hardware.
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
You can probably get a start here: https://www.youtube.com/playlist?list=PLGs0VKk2DiYxP-ElZ7-QXIERFFPkOuP4_
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?
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.
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?
I am curious to know how to use the Adafruit MotorShield to run two stepper motors or even two regular dc motors.
Typically you would connect the Adafruit Motor Shield to an Arduino, and then communicate with the Jetson. Thanks for reading!
Thanks for the video. Can you please make a video on , how to configure pin 32 & 33 as a PWM pins?
You are welcome. I believe that only pin 33 can be used for PWM.
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.
Thanks for the reply. Just to confirm, will the above mentioned PWM driver works for two dc motors?
You can try it out. There are 16 PWM channels.
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.
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)
Hi Alexey, I am having the same exact issue. Were you able to figure out a solution? Thank you!
Hi. Unfortunately, i don’t remember, does i found a solution. Check my GitHub repo:
https://github.com/format37/jetson_nano_gpio
Great, thank you for responding! 🙂
Hi
Thanks for all your hard work
Do you know how to link it with ROS ?
Thanks again
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!
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.
I don’t know off the top of my head, I believe it is 1A. Best to check on the Jetson Nano forum. Thanks for reading!
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.
It is difficult to tell from your description what the issue might be. I do not have anything else to share on this project, but you may find this video useful: https://www.youtube.com/watch?v=8YKAtpPSEOk
Good luck on your project!
Can the motor driver control 2 BLDC motors with 24-36v independently ?
I am trying to do differential steering.
I don’t know what you are asking, this is a PWM board controlled by I2C, not a motor driver board.
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.
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?
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
Have you watched this video? https://youtu.be/8YKAtpPSEOk
Thanks for reading!
Thanks for your suggestion Kangalow,
I’m gonna watch it.
I’m ALSO a huge fan of Paul 😉
bye and thanks for your help
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?
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!
It does, though you will probably need to check the GPIO pinout. Thanks for reading!
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
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.
I got the same error running on Jetson Nano, it seem the last version bug.
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 ?
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?
I’m not sure how you connected the second board. Do you know which I2C bus the second board connects to?
They are piggy backed to the same I2c connections as per adafruit instructions. That is why you need to change the board address
I don’t know why it doesn’t work. I was thinking if it was a different pin it might be on a different bus. Perhaps you can check to see if it shows up in i2cdetect.
In any case, I’m not very familiar with the RPi. A better place to ask for help is in from Adafruit or the Adafruit ServoKit Github repository: https://github.com/adafruit/Adafruit_CircuitPython_ServoKit
Good luck on your project!
Thanks for the link, I’ll give it a go.
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!
Is your user part of the GPIO group? Are you using sudo with your program?
Will this tutorial work with the Jetson Orin Nano?