DEV Community

Cover image for Running Pi4J on the NVIDIA Jetson Nano
IgorIOT
IgorIOT

Posted on

Running Pi4J on the NVIDIA Jetson Nano

Story Horse?

This post was inspired by the excellent article by WebTechie, First Test of Java on Banana Pi (ARM and RISC-V) Plus a Blinking LED with Pi4J. The idea of running Pi4J on boards other than the Raspberry Pi had been on my mind for a long time. I've had an NVIDIA Jetson Nano since around 2019 and always wanted to try it, but I never knew where to begin.I assumed supporting another SBC would require significant changes to Pi4J and be far more complicated than it was worth. After reading WebTechie's post, I realized the problem was much more approachable than I had imagined, so I finally dusted off my Jetson Nano and decided to give it a try.

Making Pi4J Platform-Agnostic or Supporting GPIO on Any Linux SBC

One of Pi4J's strengths is how easy it is to work with GPIO on the Raspberry Pi. However, as more Linux-based single-board computers become increasingly popular—such as Jetson Nano, Orange Pi, Banana Pi, Radxa, and Arduino UNO Q—the GPIO addressing model becomes much less consistent.

My Journey

I mostly followed the guide Using Pi4J on Other Brands, adapting the steps for the NVIDIA Jetson Nano. It provides a straightforward process for identifying the correct gpiochip, calculating the GPIO line offset, verifying it with libgpiod, and finally configuring Pi4J to use it.

I installed the Linux GPIO utilities to inspect the GPIO controllers available on the system:

sudo apt install -y gpiod
gpiodetect
gpioinfo
Enter fullscreen mode Exit fullscreen mode

After running these commands, I noticed that the GPIO values reported by the system did not match the values described in the blog post I was following.

After doing some research, I learned that the Jetson Nano is based on NVIDIA's Tegra SoC architecture. As a result, the GPIO numbers and pin names shown by tools such as gpioinfo can differ from examples written for other platforms or newer JetPack releases.

Searching for information about Tegra GPIO mapping eventually led me to the following NVIDIA documentation, which explains the Jetson Nano pinmux configuration and the relationship between header pins, Tegra pin names, and GPIOs:

During my research, I found the following file in the Jetson.GPIO repository.

This file already contains the GPIO mappings used by the library, including the Linux GPIO numbers associated with each Tegra pin. However, it does not explain how those values were derived.

After digging a little deeper, I eventually found the formula used to calculate the GPIO numbers, along with the GPIO port definitions. This made it possible to understand where the values in gpio_pin_data.py come from, rather than simply relying on the precomputed table.

The most difficult part, and the one I was almost unable to find from an official source, was the Tegra GPIO names.

Although NVIDIA provides documentation about the Jetson Nano hardware and pinmux configuration, finding a complete mapping between the 40-pin header, the Tegra GPIO names (such as GPIO3_PZ.01), and the corresponding Linux GPIO numbers was surprisingly difficult. Most of the available information is scattered across different documents, source files, forum posts, and the Jetson.GPIO source code, making it challenging to locate a single authoritative reference.

I learned that NVIDIA provides a utility called jetson-gpio-pinmux-lookup to simplify this process. Unfortunately, my Jetson Nano is running an older version of JetPack, which is not compatible with this utility, so I was unable to install it.

JetPack is NVIDIA's official software development kit (SDK) for Jetson platforms. It includes the Linux for Tegra (L4T) operating system, device drivers, CUDA, TensorRT, computer vision libraries, and development tools required to build and run applications on Jetson devices.

This Jetson Nano is currently running an older software release (JetPack 4.2 / L4T R32.1), which was released in 2019. This version uses Python 3.6 and an older release of the Jetson.GPIO library that does not include the jetson-gpio-pinmux-lookup utility.

I discovered that my Jetson Nano is an A02 revision running JetPack 4.2 (L4T R32.1).

GPIO pinout (image from the Internet) header J41

My Results

I was able to get it running much more easily than I had expected.

After looking into it further and thinking about how the same approach would work on boards like the Orange Pi, I realized there is usually a simple pattern to follow:

  1. Find the GPIO name used by the SoC.
  2. Look up how that SoC maps GPIO ports/banks to Linux GPIO line offsets.
  3. Apply the corresponding formula.

For example, Allwinner, Tegra, and Rockchip each have their own GPIO numbering rules, but once you know the mapping, calculating the line offset becomes straightforward.

My Thoughts

This was just a simple "Hello, World" example, a basic LED blink. Working with peripherals such as PWM, SPI, I²C, and other interfaces will be a completely different story, and I'm excited to see what Pi4J will make possible on the NVIDIA Jetson Nano. Another idea I've wanted to explore for a long time is using a Raspberry Pi HAT with the Jetson Nano. The Blinkt! HAT seems like a great place to start. Hopefully, that will be the subject of a future blog post.

That said, I think we have exciting times ahead.

As far as I know, this is the first time I've seen Pi4J running on a platform other than the Raspberry Pi, and it's also the first time I've personally run it on a non-Raspberry Pi board.

There is already a GitHub issue discussing support for additional Linux SBCs.

I'm looking forward to seeing how this evolves and what solution the Pi4J team ultimately decides to implement.

One idea that came to mind is introducing a pluggable LineMapper interface for non-Raspberry Pi platforms. Instead of embedding SoC-specific GPIO address calculations into Pi4J, platform plugins (or even applications themselves) could provide the logic to translate a GPIO address into a Linux GPIO line offset. This would keep the Pi4J core platform-agnostic while making it straightforward to support Allwinner, Tegra, Rockchip, Qualcomm, and many other Linux SBCs.

In other words, Pi4J would expose the extension point, and each platform would simply provide its own mapping logic.

There is already a draft pull request moving in this direction:

One small API consideration: since Tegra uses two-letter port names (for example AA, BB, CC, and DD), LineHelper may need to accept a String instead of a single char. That would allow more natural calls such as:

LineHelper.getAddress(GpioLineFamily.TEGRA, "CC", 0);
Enter fullscreen mode Exit fullscreen mode

So, what is the challenge?

On a Raspberry Pi, GPIO numbering is well-defined and stable. On many other boards, however, the GPIO line exposed by libgpiod depends on the SoC architecture.

For example:

  • Allwinner -- PI15 = (8 × 32) + 15 = 271
  • NVIDIA Tegra -- PJ.07 = (9 × 8) + 7 = 79
  • Rockchip RK3566 -- GPIO4_D5 = (4 × 32) + (3 × 8) + 5 = 157

Each SoC family follows its own addressing rules.

Why This Matters

Today, applications often need to manually calculate the GPIO line offset before using Pi4J with the Linux gpiod provider.

This makes applications less portable and forces developers to understand SoC-specific GPIO numbering schemes.

Perhaps, A Better Approach

Instead of embedding support for every SoC directly into Pi4J, a small extension point could make the library much more flexible.

For example:

public interface LineMapper {
    int toLineOffset(String gpioName);
}
Enter fullscreen mode Exit fullscreen mode

Sample code

Using the Code from draft PR

import com.pi4j.Pi4J;
import com.pi4j.boardinfo.util.GpioLineFamily;
import com.pi4j.boardinfo.util.LineHelper;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;

public class JetsonNanoLineHelperExample {

    public static void main(String[] args) throws Exception {
        var pi4j = Pi4J.newAutoContext();

        // Jetson Nano J41 physical pin 12 = PJ.07
        // J = bank/port index 9, line 7
        // Tegra = 9 * 8 + 7 = 79
        int gpio = LineHelper.getAddress(GpioLineFamily.TEGRA, 'J', 7);

        var led = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
            .id("led")
            .name("Jetson Nano J41 pin 12")
            .address(gpio)
            .initial(DigitalState.LOW)
            .shutdown(DigitalState.LOW)
            .build());

        led.high();
        Thread.sleep(1000);
        led.low();

        pi4j.shutdown();
    }
}
Enter fullscreen mode Exit fullscreen mode

CLI

I had to run the command using the --mode=wait option:

sudo gpioset --mode=wait gpiochip0 79=1
Enter fullscreen mode Exit fullscreen mode

The reason is that, by default, gpioset changes the GPIO state and then immediately releases the line when the process exits. Once the GPIO is released, the pin can return to its previous state depending on the kernel configuration and pinmux settings.

Using --mode=wait keeps the GPIO line claimed and maintains the requested state until the command is interrupted. This allowed me to verify the GPIO state and measure the correct voltage level on the physical pin.

gpioset gpiochip0 79=1 # ON
gpioset gpiochip0 79=0 # OFF
Enter fullscreen mode Exit fullscreen mode

Conclusion

The real difference between most Linux SBCs isn't the board itself—it's the GPIO controller inside the SoC.

It's much easier than I originally expected.

This also raises an interesting question: could we define a general approach for supporting other boards? If you can identify the SoC's GPIO naming scheme and understand how it maps GPIO ports or banks to Linux GPIO line offsets, supporting a new platform may be much more straightforward than it first appears.

Call to action

If this post has inspired you to try Pi4J on your own hardware, I'd love to hear about it! Whether you have one of the boards mentioned here or a completely different Linux SBC, don't hesitate to give it a try. If you do, please share your results, experiences, or any challenges you encounter in the comments or on GitHub. The more boards we test, the easier it will be to improve Pi4J's support for the wider SBC ecosystem.

Links

https://forums.developer.nvidia.com/t/tegra-gpio-port-offset/179621

https://raw.githubusercontent.com/NVIDIA/jetson-gpio/master/lib/python/Jetson/GPIO/gpio_pin_data.py

https://wiki.t-firefly.com/en/ROC-RK3566-PC/driver_gpio.html

Top comments (1)

Collapse
 
fdelporte profile image
Frank Delporte

Great work Igor!