Arducam B0250 IMX477 or CSI on Jetson Nano or NX

Hi,

I’ve noticed that there is support in the kernel patches of Jetson balena-jetson/jetson-dtbs.bb at master · balena-os/balena-jetson · GitHub

And i’m testing with a standard raspberry pi CSI camera

Sadly enough i’ve noticed its impossible to install the driver that is deliverd out-of-the-box due that it patches the kernel. I don’t think its possible to do this from a container isn’t it?

I’ve tested it on the v2.98.12 on a jetson nano 2gb . Nothing was detected according to dmesg and no /dev/video0 became visible on host.
After this i’ve moved to the NX v2.107.10 which didn’t recognize the CSI cam and recognized the arducam, but no /dev/video* on host. Are there any known issues with kernel patches? And are /dev/video* visible without the nvargus-daemon?

I’ve done some digging and found a list of dtb files in their installer.

The following DTS file i’ve decompiled from their deb file called Camera_IMX477-A_and_Arducam-B.dtb which sounds like the one I need. I’m compiling now the yocto with this one added to see if this has a good result.

/dts-v1/;

/ {
	overlay-name = "Camera IMX477-A and Arducam-B";
	jetson-header-name = "Jetson Nano CSI Connector";
	compatible = "nvidia,p3449-0000-b00+p3448-0000-b00\0nvidia,p3449-0000-b00+p3448-0002-b00";

	fragment@0 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@1 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			badge = "porg_front_RBPCV3";
			position = "front";
			orientation = [31 00];
		};
	};

	fragment@2 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			pcl_id = "v4l2_sensor";
			devname = "arducam 8-000c";
			proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@1/arducam_e@0c";
		};
	};

	fragment@3 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@4 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			badge = "porg_rear_RBPCV2";
			position = "rear";
			orientation = [31 00];
		};
	};

	fragment@5 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			pcl_id = "v4l2_sensor";
			devname = "imx477 7-001a";
			proc-device-tree = "/proc/device-tree/cam_i2cmux/i2c@0/rbpcv3_imx477_a@1a";
		};
	};

	fragment@6 {
		target = <0xffffffff>;

		__overlay__ {
			status = "disabled";
		};
	};

	fragment@7 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x04>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@8 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x00>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@9 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x04>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@10 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@11 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x00>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@12 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@13 {
		target = <0xffffffff>;

		__overlay__ {
			status = "disabled";
		};
	};

	__fixups__ {
		arducam_cam1 = "/fragment@0:target:0";
		cam_module0 = "/fragment@1:target:0";
		cam_module1_drivernode0 = "/fragment@2:target:0";
		imx477_cam0 = "/fragment@3:target:0";
		cam_module1 = "/fragment@4:target:0";
		cam_module0_drivernode0 = "/fragment@5:target:0";
		imx219_cam0 = "/fragment@6:target:0";
		arducam_vi_in1 = "/fragment@7:target:0";
		arducam_csi_out1 = "/fragment@7/__overlay__:remote-endpoint:0\0/fragment@10:target:0";
		rbpcv3_imx477_vi_in0 = "/fragment@8:target:0";
		rbpcv3_imx477_csi_out0 = "/fragment@8/__overlay__:remote-endpoint:0\0/fragment@12:target:0";
		arducam_csi_in1 = "/fragment@9:target:0";
		arducam_out1 = "/fragment@9/__overlay__:remote-endpoint:0";
		rbpcv3_imx477_csi_in0 = "/fragment@11:target:0";
		rbpcv3_imx477_dual_out0 = "/fragment@11/__overlay__:remote-endpoint:0";
		imx219_cam1 = "/fragment@13:target:0";
	};
};

There is an other one called common-arducam.dtb but I don’t expect this one to be relevant to the camera

/dts-v1/;

/ {
	overlay-name = "Camera Arducam";
	jetson-header-name = "Jetson Nano CSI Connector";
	compatible = "nvidia,p3542-0000+p3448-0003\0nvidia,p3449-0000-a02+p3448-0000-a02\0nvidia,p3449-0000-a01+p3448-0000-a01";

	fragment@0 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@1 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			badge = "porg_front_RBPCV3";
			position = "front";
			orientation = [31 00];
		};
	};

	fragment@2 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			pcl_id = "v4l2_sensor";
			devname = "arducam 6-000c";
			proc-device-tree = "/proc/device-tree/host1x/i2c@546c0000/arducam_a@0c";
		};
	};

	fragment@3 {
		target = <0xffffffff>;

		__overlay__ {
			num-channels = <0x01>;
		};
	};

	fragment@4 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@5 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x00>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@6 {
		target = <0xffffffff>;

		__overlay__ {
			num-channels = <0x01>;
		};
	};

	fragment@7 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@8 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@9 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			port-index = <0x00>;
			bus-width = <0x02>;
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@10 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@11 {
		target = <0xffffffff>;

		__overlay__ {
			status = "okay";
			remote-endpoint = <0xffffffff>;
		};
	};

	fragment@12 {
		target = <0xffffffff>;

		__overlay__ {
			num_csi_lanes = <0x02>;
			max_lane_speed = <0x16e360>;
			min_bits_per_pixel = <0x0a>;
			vi_peak_byte_per_pixel = <0x02>;
			vi_bw_margin_pct = <0x19>;
			max_pixel_rate = <0x3a980>;
			isp_peak_byte_per_pixel = <0x05>;
			isp_bw_margin_pct = <0x19>;
		};
	};

	fragment@13 {
		target = <0xffffffff>;

		__overlay__ {
			status = "disabled";
		};
	};

	fragment@14 {
		target = <0xffffffff>;

		__overlay__ {
			status = "disabled";
		};
	};

	__fixups__ {
		arducam_single_cam0 = "/fragment@0:target:0";
		cam_module0 = "/fragment@1:target:0";
		cam_module0_drivernode0 = "/fragment@2:target:0";
		vi_base = "/fragment@3:target:0";
		vi_port0 = "/fragment@4:target:0";
		arducam_vi_in0 = "/fragment@5:target:0\0/fragment@11/__overlay__:remote-endpoint:0";
		arducam_csi_out0 = "/fragment@5/__overlay__:remote-endpoint:0\0/fragment@11:target:0";
		csi_base = "/fragment@6:target:0";
		csi_chan0 = "/fragment@7:target:0";
		csi_chan0_port0 = "/fragment@8:target:0";
		arducam_csi_in0 = "/fragment@9:target:0";
		arducam_out0 = "/fragment@9/__overlay__:remote-endpoint:0";
		csi_chan0_port1 = "/fragment@10:target:0";
		tcp = "/fragment@12:target:0";
		imx219_single_cam0 = "/fragment@13:target:0";
		imx477_single_cam0 = "/fragment@14:target:0";
	};
};

Thank you for sharing @neonlink let us know if this works!

I added the dtb to the build. Currently only testing Nano. I had a kernel compile issue, but it was not on a fresh build. Is this the correct way? Comparing balena-os:master...minusplusminus:master · balena-os/balena-jetson · GitHub

Here’s the error. thanks :slight_smile:

WARNING: The BB_DISKMON_DIRS "ABORT" action has been renamed to "HALT", update configuration
Initialising tasks: 100% |####################################################################################################################################################| Time: 0:00:01
Sstate summary: Wanted 55 Local 0 Mirrors 0 Missed 55 Current 1852 (0% match, 97% complete)
NOTE: Executing Tasks
Setscene tasks: 1907 of 1907
ERROR: kernel-modules-headers-1.0-r0 do_compile: ExecutionError('/home/martijn/balena-jetson/build/tmp/work/jetson_nano-poky-linux/kernel-modules-headers/1.0-r0/temp/run.do_compile.1429059', 2, None, None)
ERROR: Logfile of failure stored in: /home/martijn/balena-jetson/build/tmp/work/jetson_nano-poky-linux/kernel-modules-headers/1.0-r0/temp/log.do_compile.1429059
Log data follows:
| DEBUG: Executing shell function do_compile
| Running modules_prepare...
| make[1]: Entering directory '/home/martijn/balena-jetson/build/tmp/work/jetson_nano-poky-linux/kernel-modules-headers/1.0-r0/kernel_modules_headers'
| scripts/kconfig/conf  --silentoldconfig Kconfig
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| Kconfig:6: syntax error
| Kconfig:5: invalid statement
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| init/Kconfig:2055: symbol 'MODULES' redefines option 'modules' already defined by symbol 'MODULES'
| make[3]: *** [/home/martijn/balena-jetson/build/tmp/work-shared/jetson-nano/kernel-source/scripts/kconfig/Makefile:39: silentoldconfig] Error 1
| make[2]: *** [/home/martijn/balena-jetson/build/tmp/work-shared/jetson-nano/kernel-source/Makefile:575: silentoldconfig] Error 2
| make[1]: Leaving directory '/home/martijn/balena-jetson/build/tmp/work/jetson_nano-poky-linux/kernel-modules-headers/1.0-r0/kernel_modules_headers'
| make[1]: *** No rule to make target 'include/config/auto.conf', needed by 'include/config/kernel.release'.  Stop.
| make: *** [Makefile:170: sub-make] Error 2
| WARNING: exit code 2 from a shell command.

I have it now that it recognizes the camera.

vi-output, imx477 7-001a (platform:54080000.vi:0):
        /dev/video0

v4l2-ctl --all
Driver Info (not using libv4l2):
        Driver name   : tegra-video
        Card type     : vi-output, imx477 7-001a
        Bus info      : platform:54080000.vi:0
        Driver version: 4.9.253
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
Priority: 2
Video input : 0 (Camera 0: no power)
Format Video Capture:
        Width/Height      : 3840/2160
        Pixel Format      : 'RG10'
        Field             : None
        Bytes per Line    : 7680
        Size Image        : 16588800
        Colorspace        : sRGB
        Transfer Function : Default (maps to sRGB)
        YCbCr/HSV Encoding: Default (maps to ITU-R 601)
        Quantization      : Default (maps to Full Range)
        Flags             : 

Camera Controls

                     group_hold 0x009a2003 (bool)   : default=0 value=0 flags=execute-on-write
                    sensor_mode 0x009a2008 (int64)  : min=0 max=0 step=0 default=0 value=0 flags=slider
                           gain 0x009a2009 (int64)  : min=0 max=0 step=0 default=0 value=16 flags=slider
                       exposure 0x009a200a (int64)  : min=0 max=0 step=0 default=0 value=13 flags=slider
                     frame_rate 0x009a200b (int64)  : min=0 max=0 step=0 default=0 value=2000000 flags=slider
                    bypass_mode 0x009a2064 (intmenu): min=0 max=1 default=0 value=0
                override_enable 0x009a2065 (intmenu): min=0 max=1 default=0 value=0
                   height_align 0x009a2066 (int)    : min=1 max=16 step=1 default=1 value=1
                     size_align 0x009a2067 (intmenu): min=0 max=2 default=0 value=0
               write_isp_format 0x009a2068 (bool)   : default=0 value=0
       sensor_signal_properties 0x009a2069 (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
        sensor_image_properties 0x009a206a (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
      sensor_control_properties 0x009a206b (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
              sensor_dv_timings 0x009a206c (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
               low_latency_mode 0x009a206d (bool)   : default=0 value=0
               preferred_stride 0x009a206e (int)    : min=0 max=65535 step=1 default=0 value=0
                   sensor_modes 0x009a2082 (int)    : min=0 max=30 step=1 default=30 value=3 flags=read-only

I did this with the following steps:
downloaded deb file from this list https://github.com/ArduCAM/MIPI_Camera/releases/download/v0.0.3/imx477_links.txt

unpacked deb file ar x {name}.deb
inside the deb file there is a data tar. I unpacked it and copied everything to a jetson with regular OS.
Then I copied the files to the sd card according to the directory structure.
there is a file called control/postinst which can be executed:

#!/bin/bash

# set -x
RED='\033[0;31m'
NC='\033[0m' # No Color

pushd /boot/extlinux &> /dev/null

EXTLINUX=$(ls extlinux.conf)

if [ -n "${EXTLINUX}" ]; then
        if [ ! -e ${EXTLINUX}_bak ]; then
                cp ${EXTLINUX} ${EXTLINUX}_bak
        fi
        cp /boot/arducam/extlinux.conf ${EXTLINUX}
        popd &> /dev/null
fi

pushd /boot &> /dev/null
IMAGE=$(ls Image)

if [ -n "${IMAGE}" ]; then
        if [ ! -e ${IMAGE}.bak ]; then
                cp ${IMAGE} ${IMAGE}.bak
        fi
        popd &> /dev/null
fi

result=$(sudo /opt/arducam/jetson-io/config-by-hardware.py --list | grep -E "Header\s*([0-9]+).*Jetson Nano CSI Connector")
regex="Header\s*([0-9]+)?.*:\s*Jetson Nano CSI Connector"
headerNumber=2
if [[ $result =~ $regex ]]
then
        headerNumber=${BASH_REMATCH[1]}
else
        echo "Jetson Nano CSI Connector not found."
        sudo /opt/arducam/jetson-io/config-by-hardware.py --list
fi
echo $headerNumber

/opt/arducam/jetson-io/config-by-hardware.py -n $headerNumber="Camera IMX477 Dual" &> /dev/null
if [ $? -ne 0 ]; then
    /opt/arducam/jetson-io/config-by-hardware.py -n $headerNumber="Camera IMX477" &> /dev/null
    if [ $? -ne 0 ]; then
        echo ""
        echo -e "${RED}An unknown error occurred while installing dtoverlays.${NC}"
        exit -1
    fi
fi

exit 0

Or you can run te jetson-io UI:
I executed sudo /opt/adrucam/jetson-io/jetson-io.py which shows the menu. I installed the drivers. It generated a custom dtb file inside /boot/arducam and set this in set it in extlinux.conf as FDT /boot/tegra210-p3448-0000-p3449-0000-b00-user-custom.dtb on BalenaOS 2.103.2

dockerfile is jetson-examples/Dockerfile at master · balena-io-examples/jetson-examples · GitHub

docker-compose:

version: '2'
volumes:
  resin-data: { }

services:
  demo:
    hostname: "demo"
    build: ./
    user: root
    privileged: true
    cap_add:
      - SYS_RAWIO
    environment:
      - UDEV=1
    expose:
      - 19912
    devices:
      - "/dev:/dev"
    labels:
      io.balena.features.sysfs: '1'
      io.balena.features.kernel-modules: '1'
      io.balena.features.firmware: '1'
      io.balena.features.supervisor-api: '1'
      io.balena.features.balena-api: '1'
      io.balena.features.dbus: '1'

export DISPLAY=:0.0
jetson_clocks
nvargus-daemon &
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! 'video/x-raw(memory:NVMM),width=1920, height=1080, framerate=30/1, format=NV12' ! nvvidconv ! 'video/x-raw(memory:NVMM),format=I420' ! fpsdisplaysink text-overlay=0 video-sink=fakesink sync=0 -v
libEGL warning: DRI2: failed to authenticate
=== NVIDIA Libargus Camera Service (0.98.3)=== Listening for connections...=== nvgstcapture[3258]: Connection established (7F666EF1D0)OFParserListModules: module list: /proc/device-tree/tegra-camera-platform/modules/module0
OFParserListModules: module list: /proc/device-tree/tegra-camera-platform/modules/module1
NvPclHwScanExternalCameras: Failed to stat '/dev//log'; No such file or directory
NvPclHwScanExternalCameras: Failed to stat '/dev//initctl'; No such file or directory
NvPclHwScanExternalCameras: Failed to stat '/dev//log'; No such file or directory
NvPclHwScanExternalCameras: Failed to stat '/dev//initctl'; No such file or directory
NvPclHwScanExternalCameras: Failed to stat '/dev//log'; No such file or directory
NvPclHwScanExternalCameras: Failed to stat '/dev//initctl'; No such file or directory
OFParserGetVirtualDevice: NVIDIA Camera virtual enumerator not found in proc device-tree
---- imager: No override file found. ----
---- imager: No override file found. ----
(NvCamV4l2) Error ModuleNotPresent: V4L2Device not available (in /dvs/git/dirty/git-master_linux/camera/utils/nvcamv4l2/v4l2_device.cpp, function findDevice(), line 256)
(NvCamV4l2) Error ModuleNotPresent:  (propagating from /dvs/git/dirty/git-master_linux/camera/utils/nvcamv4l2/v4l2_device.cpp, function initialize(), line 60)
(NvOdmDevice) Error ModuleNotPresent:  (propagating from dvs/git/dirty/git-master_linux/camera-partner/imager/src/devices/V4L2SensorViCsi.cpp, function initialize(), line 107)
NvPclDriverInitializeData: Unable to initialize driver v4l2_sensor
NvPclInitializeDrivers: error: Failed to init camera sub module v4l2_sensor
NvPclStartPlatformDrivers: Failed to start module drivers
NvPclStateControllerOpen: Failed ImagerGUID 0. (error 0xA000E)
NvPclOpen: PCL Open Failed. Error: 0xf
SCF: Error BadParameter: Sensor could not be opened. (in src/services/capture/CaptureServiceDeviceSensor.cpp, function getSourceFromGuid(), line 593)
SCF: Error BadParameter:  (propagating from src/services/capture/CaptureService.cpp, function addSourceByGuid(), line 437)
SCF: Error BadParameter:  (propagating from src/api/CameraDriver.cpp, function addSourceByIndex(), line 305)
SCF: Error BadParameter:  (propagating from src/api/CameraDriver.cpp, function getSource(), line 471)
Acquiring SCF Camera device source via index 1 has failed. === nvgstcapture[3258]: CameraProvider initialized (0x7f5094cc90)SCF: Error BadValue: NvPHSSendThroughputHints (in src/common/CameraPowerHint.cpp, function sendCameraPowerHint(), line 56)
(Argus) Error NotSupported: Failed to get function:eglCreateStreamKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
(Argus) Error NotSupported: Failed to get function:eglDestroyStreamKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
(Argus) Error NotSupported: Failed to get function:eglQueryStreamKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
(Argus) Error NotSupported: Failed to get function:eglStreamAttribKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
(Argus) Error NotSupported: Failed to get function:eglGetStreamFileDescriptorKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
(Argus) Error NotSupported: Failed to get function:eglCreateStreamFromFileDescriptorKHR
 (in src/eglutils/EGLUtils.cpp, function ext(), line 199)
Segmentation fault (core dumped

fixed it:

echo "/usr/lib/aarch64-linux-gnu/tegra-egl" > /etc/ld.so.conf.d/nvidia-tegra-egl.conf && ldconfig
export DISPLAY=:0.0
nvargus-daemon &
startx &
SENSOR_ID=0 # 0 for CAM0 and 1 for CAM1 ports
FRAMERATE=60 # Framerate can go from 2 to 60 for 1920x1080 mode
gst-launch-1.0 nvarguscamerasrc sensor-id=$SENSOR_ID ! "video/x-raw(memory:NVMM),width=1920,height=1080,framerate=$FRAMERATE/1" ! nvvidconv ! nvoverlaysink

1 Like

Great to hear that you managed to make it work, Martijn! Thanks for sharing!

1 Like

@neonlink So to recap, you

  1. generated or exported the .dtb file: tegra210-p3448-0000-p3449-0000-b00-user-custom.dtb
  2. loaded that on the Balena powered Jetson
  3. Ran it as per: Arducam B0250 IMX477 or CSI on Jetson Nano or NX - #8 by neonlink

Does that sum it up?

@gantonayde Are there plans to have that .dtb as part of the baseOS?

1 Like

Sure, we can have the dtb as part of the hostOS, please open a PR with those changes.