IDE
Raspberry PI Cross Development Environmentt

Raspberry PI Cross Development Environmentt

Equipment :

  • Raspberry pi 4 ( user : rpi, static IP address : 192.168.0.2 )
  • PC Side : Ubuntu 22.04.2 LTS ( static IP address : 192.168.0.1 )

1 - Talking with the target

In following examples, static address for PC is 192.168.0.1.
It can be modified with $ sudo ifconfig [Eth_name] 192.168.0.1

1.1 - Connection

NOTE : ssh must be activated on RPI

$ ssh rpi@192.168.0.2

Graphical mode :

$ ssh -X rpi@192.168.0.2

1.2 - Copies

Copy PC –> RPI

$ scp file_to_copy rpi@192.168.0.2:/home/rpi

Copy Files PC <–> RPI

$ scp file_to_copy rpi@192.168.0.2:/home/rpi

filezilla:

Host : 192.168.0.2
Username : rpi
Password :
Port : 22

1.3 - ssh/scp without password

RPI (via ssh)

$ sudo nano /etc/ssh/sshd_config
PermitRootLogin yes
$ sudo service ssh restart

PC

$ ssh-keygen -t rsa
# entry for passphrase
$ ssh-copy-id -i ~/.ssh/id_rsa.pub rpi@192.168.0.2

1.4 - Eduroam wifi Configuration

$ nano /etc/wpa_supplicant/wpa_supplicant.conf

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=FR

network={
  ssid="eduroam"
  disabled=0
  auth_alg=OPEN
  ssid="eduroam"
  scan_ssid=1
  key_mgmt=WPA-EAP
  proto=WPA RSN
  pairwise=CCMP TKIP
  eap=PEAP
  identity="[your_id]"                  # Edit this,
  anonymous_identity="anonymous@enib.fr"            
  password="[your_pw]"  				# and this.
  phase1="peaplabel=0"
  phase2="auth=MSCHAPV2"
}

2 - Compiling on Target

Considering the following helloworld.cpp file :

#include <iostream>
#include <unistd.h>

using namespace std;

int main (void)
{
    int i = 0;
    cout << "Hello RPi Development World !"<< endl;

    cout << "Hello RPI Development World Again !" << endl;

    while(1)
    {
        cout << "I'm in the While loop and the value of variable i is: " << i << endl;
        i++;
        usleep(1000000); //wait for 1 seconds
    }

    return 0;

}

let’s copy it on the target :

$ scp helloworld.cpp rpi@192.168.0.2:/home/rpi

RPI side (via SSH)

The RPI compiler is of course ARM type :

$ gcc -v
Target: aarch64-linux-gnu
gcc version 11.3.0
$ g++ -v
Target: aarch64-linux-gnu
gcc version 11.3.0

We compile, we execute :

$ g++ helloworld.cpp -o helloworld
$ chmod +x helloworld
$ ./helloworld
Hello RPi Development World !
Hello RPI Development World Again !
I'm in the While loop and the value of variable i is: 0
I'm in the While loop and the value of variable i is: 1
I'm in the While loop and the value of variable i is: 2
...

3 - Cross Compilation

Now let’s see how to create the RPI program on the PC.

Interest:

  • Embedded linux target don’t have necessarily graphical interface, you should feel more comfortable editing code with something else different from vi or nano.
  • Even with a graphical interface, RPI is slower than a PC.

An evidence : The PC has a compilation toolchain for Pentium.

$ gcc -v
Target: x86_64-linux-gnu

3.1 - ARM 64 ( aarch 64 ) linux Compiler

Installation

the easiest way :

$ sudo apt-get install g++-aarch64-linux-gnu

NOTE : alternatively you can download and build yourself your own compiler, or download a pre built version at linaro.org or developer.arm.com ( be aware of compiling issues due to compiler versions ).

$ aarch64-linux-gnu-g++ -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gnu-g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/aarch64-linux-gnu/11/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.3.0-1ubuntu1~22.04.1' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --without-target-system-zlib --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=aarch64-linux-gnu --program-prefix=aarch64-linux-gnu- --includedir=/usr/aarch64-linux-gnu/include --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1) 

3.2 - helloworld

PC Side

$ aarch64-linux-gnu-g++ helloworld.cpp -o helloworld
$ file helloworld
helloworld: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=89972f3af5d332f669cca9824f2177149492904d, for GNU/Linux 3.7.0, not stripped
$ chmod +x helloworld
$ scp helloworld rpi@192.168.0.2:/home/pi

RPI Side (via SSH)

$ ./helloworld
Hello RPi Development World !
Hello RPI Development World Again !
I'm in the While loop and the value of variable i is: 0
I'm in the While loop and the value of variable i is: 1
I'm in the While loop and the value of variable i is: 2

So this is exactly the same for a simple hello world.

Makefile version :

hello_world.zip

3.3 - with libraries ( ex : opencv )

It gets complicated ; let’s consider the opencv library.
The executable creation must be done with RPI opencv libs ( not the PC ones).

3.3.1- RPI Side

opencv installation

$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt-get install libcv-dev python-opencv libopencv-dev opencv-doc

3.3.2 - PC Side: RPI File system with sshfs

A solution could be copying RPI libs on the PC in order to create a minimal file system. This supposes dealing with dependances and update links to get relative paths.
An other solution, more elegant, is mounting the RPI file system through ssh thanks to sshfs.

Installation

$ sudo apt­-get install sshfs
$ sudo addgroup fuse
$ sudo usermod -a -G fuse kerhoas
$ mkdir ~/rpi-mnt
   
$ sudo nano /etc/fuse.conf 
user_allow_other

Utilisation

$ sshfs rpi@192.168.0.2:/ ~/rpi-mnt -o transform_symlinks -o allow_other
$ ls rpi-mnt 
bin   debian-binary  etc   lib         man    mnt  proc  run   srv  tmp  var
boot  dev            home  lost+found  media  opt  root  sbin  sys  usr

This is the RPI file system ; it is mounted in PC rpi_mnt directory

Compilation

WORKSPACE_TSI_OPENCV_CROSS.zip

let’s have à look to the Makefile:

# A lancer au préalable :
# sshfs rpi@192.168.0.2:/ ~/rpi-mnt -o transform_symlinks -o allow_other

CPP= aarch64-linux-gnu-g++
CC= aarch64-linux-gnu-gcc

# Path to RPI file system ( via sshfs )
RPI_FS = /home/kerhoas/rpi-mnt
#=======================================================================
SYSROOT = --sysroot=$(RPI_FS)  
CFLAGS = -Wall
EXEC_NAME = test_opencv
INCLUDES = -I$(RPI_FS)/usr/include/opencv4 -I$(RPI_FS)/usr/include/aarch64-linux-gnu -I/Includes.hpp -I/base.hpp -I/image.hpp -I/video.hpp -I/base.hpp   
LIBS = -lopencv_core -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_flann -lopencv_imgcodecs -lopencv_imgproc -lopencv_videoio
LIBSDIR= -L$(RPI_FS)/usr/include/opencv4 -L$(RPI_FS)/usr/include/x86_64-linux-gnu -L$(RPI_FS)/usr/lib64

# pour compiler des libraries appelées par des libraries :
# XLINKER = -Xlinker -rpath-link=$(RPI_FS)/lib/aarch64-linux-gnu 
# XLINKER += -Xlinker -rpath-link=$(RPI_FS)/usr/lib/aarch64-linux-gnu
# XLINKER += -Xlinker -rpath-link=$(RPI_FS)/opt/vc/lib
# XLINKER += -Xlinker -rpath-link=$(RPI_FS)/usr/include/opencv4
#=======================================================================
OBJ_FILES = main.o base.o image.o video.o
INSTALL_DIR = .

all : $(EXEC_NAME)

clean : 
	rm $(EXEC_NAME) $(OBJ_FILES)

$(EXEC_NAME) : $(OBJ_FILES)
	$(CPP) $(SYSROOT) -g $(LIBSDIR) -o $(EXEC_NAME) $(OBJ_FILES) $(LIBS)

%.o: %.cpp
	$(CPP) $(CFLAGS) -g $(INCLUDES) -o $@ -c $<	

%.o: %.c
	$(CC) $(CFLAGS) -g $(INCLUDES) -o $@ -c $<

install :
	scp $(EXEC_NAME) rpi@192.168.0.2:/home/rpi
#=======================================================================

Notes :

  • ARM compiler
  • file system reference( –sysroot)
  • make install permits copying the executable on the target through ssh.

let’s go:

$ export RPI_FS=~/rpi-mnt
$ make
aarch64-linux-gnu-g++  --sysroot=/home/kerhoas/rpi-mnt   -I/home/kerhoas/rpi-mnt/usr/include/opencv4 -I/home/kerhoas/rpi-mnt/usr/include/aarch64-linux-gnu -I/Includes.hpp -I/base.hpp  -Wall -g -o main.o -c main.cpp	
aarch64-linux-gnu-g++  --sysroot=/home/kerhoas/rpi-mnt   -I/home/kerhoas/rpi-mnt/usr/include/opencv4 -I/home/kerhoas/rpi-mnt/usr/include/aarch64-linux-gnu -I/Includes.hpp -I/base.hpp  -Wall -g -o base.o -c base.cpp	
aarch64-linux-gnu-g++  -Wall --sysroot=/home/kerhoas/rpi-mnt    -g -L/home/kerhoas/rpi-mnt/usr/lib/aarch64-linux-gnu/ -L/home/kerhoas/rpi-mnt/lib/aarch64-linux-gnu/ -L/home/kerhoas/rpi-mnt/usr/lib64  -o test_opencv main.o base.o -lopencv_core -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_flann -lopencv_imgcodecs -lopencv_imgproc -lopencv_videoio 
$ make install
scp test_opencv pi@192.168.0.2:/home/pi
test_opencv                100%  398KB  19.7MB/s   00:00 

Test on Target

$ ssh -X rpi@192.168.0.2                                                                                                                                    
$ ./test_opencv                                                                                                                                  

test_opencv.png

NOTE :

The same project for a native compiling :

test_opencv_local.zip


4 - Debug with gdbserver

Ok we compiled, but how to debug our program ( other than with printf) ?

4.1 - Installations

RPI side

$ sudo apt-get install gdbserver

PC side

$ sudo apt-get install gdb-multiarch

4.2 - with tdb

you can get it in installations.html ( 1. IDE Eclipse / Tdb )

RPI side

$ gdbserver 192.168.0.1:2345 test_opencv B
Process test_opencv created; pid = 1514
Listening on port 2345

PC side

In the project directory, create a .tdbinit file :

$ nano .tdbinit                              
#! gdb-multiarch
set sysroot $RPI_FS
target remote 192.168.0.2:2345
tbreak main
continue
$ export RPI_FS=~/rpi-mnt
$ tdb test_opencv 
debugger =  gdb-multiarch                                                                                                                                

tdb.png

4.3 - With Eclipse ( STM32CUBEIDE or Eclipse C/C++ 2023 )

RPI side

$ gdbserver 192.168.0.1:2345 test_opencv B
Process test_opencv created; pid = 1514
Listening on port 2345

PC side

$ stm32cubeide

Eclipse:

  • run –> Debug configuration : C/C++ remote application
  • Using GDF (DSF) Manual Remote Debugging Launcher
  • Debugger : gdb-multiarch
  • connection : Generic TCP, 192.168.0.2, 2345
  • Startup : no reset, no halt

Toogle breakpoint at the main beginning. After loading program, press F8 ( continue ), you should go to the breakpoint.
Each time you debug, don’t forget to execute gdbserver on raspberry pi.

eclipse2.png