Skip to content

ODroid Setup

Andrea L'Afflitto edited this page Dec 31, 2025 · 3 revisions

ODroid M1S SetupSetting up UXRCE-DDS

1. Flashing the OS to the ODroid M1S

Things you will need:

  • USB A - Micro B OTG cable
  • Power for the ODroid
  • Balena Etcher
  • SD card (8GB or above)
  • PC (will be called groundstation throught the rest of the document)

Caution

The ODroid M1s features onboard eMMC storage. To gain read/write permissions for uploading the OS onto the ODroid M1s, a special SD card with a flashed image is required.

Warning

Please ensure that the image transfer process in Balena Etcher is fully completed before unplugging the ODroid from power.

Tip

Balena Etcher is used as the example software here. However, you can use other tools as well, such as Raspberry Pi Imager if you are comfortable with the software.

Procedure:

  1. Download the ODROID-M1S_EMMC2UMS.img and flash it to the SD card using Balena Etcher. This SD card is analogous to a hardware key that puts the ODroid's eMMC in read/write mode.
  2. Ensure the ODroid M1S is powered off. Plug the SD card with the newly flashed image into the card slot of the ODroid M1S. Then power the board using the power adapter or by plugging it into the groundstation PC.
  3. Plug in the USB A - Micro B OTG cable into the OTG port of the ODroid M1s and connect it to the groundstation pc. You should then see the ODroid M1s eMMC show up as a mass storage device. i.e, it lets you write directly to the eMMC on board.
  4. Download the latest image from the ODroid Wiki. Please take care to download the server flavor of the Ubuntu distros that are available.
  5. Follow the normal flashing procedure, using the image from Step 4 to flash the mass storage device from Step 3.
  6. Once it is done flashing the new image onto the eMMC, disconnect power to the ODroid M1s and only then unplug the SD card. Your ODroid is now flashed.

2. Configuration of OS image

Things you will need:

  • Keyboard
  • HDMI cable
  • Monitor
  • Ethernet connection
  • USB-UART Module
  • ODroid M1s RTC battery

Note

The username is odroid and the password is odroid. You'll be prompted to login on boot.

Caution

  • During the first boot, connect only the HDMI port and the Ethernet cable. Wait for the system to boot, then connect the keyboard to the USB 3.1 port and the WiFi module to the middle USB 2.0 port. You can distinguish between the ports by their color: USB 3.0 is blue, and USB 2.0 is black.
  • Have the RTC battery unplugged.

2.1 Setting up time

Connect the ODroid to a monitor and keyboard, power it on via the USB‑C port, then connect it to the internet using an Ethernet cable. Once logged in, verify the time by running

date

It should show an output that is similar to

Fri May 16 13:30:44 EDT 2025

We need to pay attention to the time zone here, EDT. Make sure it's on the right time-zone, if not set it using,

sudo dpkg-reconfigure tzdata

A dialogue box will pop up with the various countries and time zones. Use the arrow keys to navigate and the Enter key to select, then verify that the time zone is set correctly with the date command again.

Note

Since Virginia Tech follows New York time, the output, once the date is set looks like this

Current default time zone: 'America/New_York'
Local time is now:      Fri May 16 13:32:35 EDT 2025.
Universal Time is now:  Fri May 16 17:32:35 UTC 2025.

2.2 Add user as superuser

Be sure to add yourself in the tty and dialout groups via usermod to avoid having to execute scripts as root.

sudo usermod -a -G tty odroid
sudo usermod -a -G dialout odroid

Reboot the system after by typing in

sudo reboot now

Tip

If the Odroid has trouble booting up, remove the power, disconnect the keyboard and WiFi module, and then reconnect the power. Once it has booted up, plug the keyboard and WiFi module back into their respective ports.

2.3 Update the system packages

Update the system using

Important

  • Use the exact commands given below to upgrade the system.
  • Have the Wifi 5BK module pluggedd in.
sudo apt update
sudo apt-get upgrade

Note

If prompted enter the password and if there is a query that asks to "accept changes" input y using the keyboard and press the enter key. That should start the update.

2.4 Install some necessary packages

Update the repositories first

sudo apt-get update

Install the following packages

sudo apt-get install -y git nano libasio-dev usbutils linux-firmware dkms software-properties-common lsb-release build-essential make g++ gcc

after this step is completed, install the linux-headers.

sudo apt-get install linux-headers-$(uname -r)

Now, do a full system upgrade

sudo apt-get update
sudo apt-get full-upgrade

Note

git: Distributed version control system for tracking changes in source code during software development.

nano: Simple, user-friendly command-line text editor for editing files in the terminal.

libasio-dev: Development headers for Asio, a modern C++ library for asynchronous network and low-level I/O programming.

usbutils: Utilities for inspecting and listing devices connected to the USB bus, such as lsusb.

linux-firmware: Collection of binary firmware files required for the proper operation of various hardware devices.

dkms: Framework that automatically rebuilds and installs kernel modules when the kernel is upgraded.

software-properties-common: Scripts and tools for managing software repositories and PPAs on Debian-based systems.

lsb-release: Utility to display Linux Standard Base and distribution-specific information.

build-essential: Meta-package that installs essential tools (like gcc, g++, make) needed to compile C/C++ software.

make: Utility that automates the building and compilation of programs using instructions from a Makefile.

g++: GNU C++ compiler for compiling C++ source code.

gcc: GNU Compiler Collection’s C compiler for compiling C source code.

linux-headers-$(uname -r): Installs the Linux kernel header files for your current running kernel, which are required to compile kernel modules and interface properly with kernel APIs

2.5 Set up the RTC circuit

Warning

Please setup the time by following the instructions in section 2.1 before proceeding with setting up the RTC clock. Furthermore, make sure the Ethernet cable is plugged in.

Installing the RTC Battery on ODroid M1S

  1. Locate the RTC Battery Connector
    The RTC battery connector is on the back of the board. The plug is keyed, meaning it can only be inserted in one direction.

  2. Install the Battery Plug Carefully

  • Gently insert the plug into the connector.
  • ⚠️ The RTC plug is fragile — handle it with care to avoid damage.
  1. Mount the Battery
  • Use the double-sided tape provided on the back of the battery.
  • Stick the battery to the back of the board.
  • Avoid areas with PCB traces or other components.

Tip

Do not place the battery in the rectangular space allocated for the PCIe slot if you plan on using it for storage on a later date.

Caution

Ensure the battery is secure and nothing is obstructing other connections or components on the board.

Note

We want a RTC clock as the logs and udp communications utilized by the flightstack need accurate real-time timestamps.

Once the battery is mounted, it should power up the RTC circuit and have it started. We can now proceed to configure the ODroid to use the clock for time keeping.

sudo hwclock -w -f /dev/rtc0

This should write our system time to the RTC. We now need to switch off the option to sync time from the internet.

sudo timedatectl set-ntp false

Important

Do not forget to switch off the option to sync from the internet. This ensures that the OS does not look for a network time sync during boot time. This makes booting up faster and also provides us the ability to have accurate times without relying on the internet which we will not be accessing during tests.

Now run the following command to ensure that the RTC and the system time are sync'd up.

sudo hwclock -r; date

Important

Make sure the time and date on the first line matches the second line of the output in the terminal

The final step is to write some code that makes the system time sync to the time of the RTC clock upon boot and not the internet. Run the following code in the terminal.

sudo nano /etc/rc.local

This will open up a file in nano. Scroll down all the way to the end and add the following before the exit 0 line.

# Start RTC sync on bootup
if [ -f /aafirstboot ]; then /aafirstboot start ; fi
hwclock -s -f /dev/rtc0
exit 0 # <------ Already there. Do not copy this. Here to provide context.

Caution

Do not copy the exit 0 in the previous code block into the /etc/rc.local file.

The RTC is now set, shut down the ODroid, remove the ethernet cable and the power, wait for a while (5 min should be good). Power it up again and ensure the time and date are correct.

2.6 Installing CMake 3.26

We need a very particular version of CMake from Kitware, the maintainers of CMake, to have everything work well. Let's install it. Start with some pre-requisite packages:

sudo apt-get update
sudo apt-get install -y ca-certificates gpg wget

Add the Kitware GPG key using the trusted-gpg location

Get the Kitware key using wget

wget https://apt.kitware.com/keys/kitware-archive-latest.asc

Check that the file is not empty:

ls -lh kitware-archive-latest.asc
cat kitware-archive-latest.asc | head

You should see the -----BEGIN PGP PUBLIC KEY BLOCK----- header.

Now convert the key to a GPG keyring and move it:

gpg --dearmor kitware-archive-latest.asc
sudo mv kitware-archive-latest.asc.gpg /usr/share/keyrings/kitware-archive-keyring.gpg

Add the Kitware repository and update our local repository cache

Note

press enter after the \ in the command below and that will get you to the next line to continue typing in the command. This is done as the command is really long to type in one go.

echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | \
  sudo tee /etc/apt/sources.list.d/kitware.list >/dev/null
sudo apt-get update

Now, we can proceed to the installation. List available CMake versions from Kitware's repository:

apt-cache policy cmake | grep 3.26.*

This will check if CMake 3.26 is available. If it isn't available, you will have to build it from source, the instructions for which are in the troubleshooting section. We can now proceed to install cmake-3.26 and cmake-data-3.26 which is the dependency to have it all work.

sudo apt-get install cmake=3.26.* cmake-data=3.26.*

Once this is done, we need to pin this version in the apt-repository to prevent it from upgrading when we run package updates later on down the line

echo -e "Package: cmake\nPin: version 3.26*\nPin-Priority: 1001" | \
  sudo tee /etc/apt/preferences.d/cmake-pin
echo -e "Package: cmake-data\nPin: version 3.26*\nPin-Priority: 1001" | \
  sudo tee /etc/apt/preferences.d/cmake-data-pin

Finally, check if the version of cmake is valid:

cmake --version

It should output cmake version 3.26.x, here the x is the latest release of 3.26 and isn't important.

2.7 Setup the Access Point

First, let's check if the Wi-Fi module is detected

lsusb | grep "Realtek"

It should show you an output, then we need to make sure our kernel is running the module for the 8821cu module.

sudo modprobe 8821cu

If there is no output, do the following:

echo 8821cu | sudo tee -a /etc/modules
sudo modprobe 8821cu

Then reboot the system

sudo reboot now

Run the following to check if the module is loaded on reboot

lsmod | grep 8821cu

You will get two lines with 8821cu highlighted. Then you can check if the wlan0 interface is up by typing in

ip a

It will show you the wlan0 in DOWN state at the third line by default. Then you can proceed to turn it on by writing the following:

sudo nano /etc/systemd/system/wlan0-up.service

Enter the following code into the file and save and exit:

[Unit]
Description=Bring up wlan0 at boot
After=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/ip link set wlan0 up
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Start the service by running:

sudo systemctl daemon-reload
sudo systemctl enable wlan0-up.service
sudo systemctl start wlan0-up.service

Reboot the system

sudo reboot now

Then confirm it is up using

ip a

It will show you the wlan0 in UP state on the third line. We can now finally start with getting the access point installed. Install some prerequisites.

Note

Some of the necessary packages are installed in section 2.4. If you didn't install them go back and do so.

sudo apt install -y libgtk-3-dev pkg-config hostapd libqrencode-dev libpng-dev
sudo apt install -y iw iptables

Then finally, install dnsmasq

sudo apt install -y dnsmasq

Important

If you faced “FAILED” message when starting up the dnsmasq.service like the below “port 53: Address already in use”,

invoke-rc.d: initscript dnsmasq, action "start" failed.
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
   Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mon 2018-12-10 01:59:06 UTC; 22ms ago
  Process: 2073 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=2)
  Process: 2072 ExecStartPre=/usr/sbin/dnsmasq --test (code=exited, status=0/SUCCESS)
 
Dec 10 01:59:06 odroid systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Dec 10 01:59:06 odroid dnsmasq[2072]: dnsmasq: syntax check OK.
Dec 10 01:59:06 odroid dnsmasq[2073]: dnsmasq: failed to create listening socket for port 53: Address already in use
Dec 10 01:59:06 odroid dnsmasq[2073]: failed to create listening socket for port 53: Address already in use

Here we see that port 53 is busy. We need to stop the process there. Let's find out the process that is taking up that port by running

# Check that systemd-resolve service is listening port 53 now(127.0.0.53:53)
sudo netstat -alnp | grep -w LISTEN | grep 53

You will find the process listed at the end of the line, for us it was systemd-resolve at the time of writing, to stop it type:

sudo systemctl disable systemd-resolved.service
sudo systemctl stop systemd-resolved

Now, enable dnsmasq again

sudo systemctl enable dnsmasq
sudo systemctl start dnsmasq

Confirm it is running by typing int

sudo netstat -alnp | grep -w LISTEN

Set your system to use dnsmasq by pointing /etc/resolv.conf to localhost:

sudo nano /etc/resolv.conf

In that file, type and save the following

nameserver 127.0.0.1

Then, for connecting to the internet with a DHCP, create a file as follows

sudo nano /etc/resolv.dnsmasq

Input and save the following int resolv.dnsmasq

nameserver 8.8.8.8
nameserver 8.8.4.4

Then, in /etc/dnsmasq.conf:

sudo nano /etc/dnsmasq.conf

Go to the end of the file and input the following

# Resolve file pointer for upstream network resolution
resolv-file=/etc/resolv.dnsmasq

Save and exit. Reboot the system using sudo reboot now. Once you're logged back in,

sudo systemctl status dnsmasq

Make sure there are no error or warning messages. The output should look something like this

● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2025-05-18 21:46:55 EDT; 11min ago
    Process: 387 ExecStartPre=/usr/sbin/dnsmasq --test (code=exited, status=0/SUCCESS)
    Process: 401 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=0/SUCCESS)
    Process: 461 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf (code=exited, status=0/SUCCESS)
   Main PID: 460 (dnsmasq)

Now, we can proceed to clone the repository for creating the AP

git clone https://github.com/lakinduakash/linux-wifi-hotspot.git

Start building by

cd linux-wifi-hotspot/src/scripts/
nano create_ap.conf

and modify the following as you please

SSID=MyAccessPoint
PASSPHRASE=12345678

to the following

SSID=odroid_hil_ap
PASSPHRASE=odroid_hil_ap

Tip

Choose your SSID and password as you prefer. We recommend using "NameOfPlatform_ap" as the SSID. For example, I have the ODroid M1S in this example to work for HIL, so I chose the SSID "odroid_hil_ap." We also strongly recommend setting the password to be the same as the SSID.

Caution

Make sure that both your SSID and PASSPHRASE are more than 6 characters in length. Otherwise, the code will not create the access point.

You can now proceed to compile and install the access point

# Navigate to the root of the cloned repo - here we are assuming you cloned it in the root directory as per previous instructions
cd ~/linux-wifi-hotspot

# Compile the code
sudo make install-cli-only

# Create teh AP
sudo create_ap --no-virt wlan0 eth0 odroid_hil_ap odroid_hil_ap

Note

Make sure you enter the SSID and PASSPHRASE to the one you had in the create_ap.conf file

This will have the service running, to make it persistent, open another terminal instance by pressing ctrl + alt + f2. Put your login and password, which are both odroid, and then proceed to type in the following

sudo systemctl enable create_ap
sudo systemctl start create_ap

Then reboot the system sudo reboot now, once you are logged back in, check if your access point service is running

sudo systemctl status create_ap

It should look something like this

● create_ap.service - Create AP Service
     Loaded: loaded (/lib/systemd/system/create_ap.service; enabled; vendor pre>
     Active: active (running) since Sun 2025-05-18 21:46:54 EDT; 10min ago
   Main PID: 383 (create_ap)

You can now connect to the access point like using its name and password, and log in to the ODroid remotely using the following command

ssh odroid@192.168.12.1

The password is odroid.

2.8 USB-UART Communication and System Rule for Pixhawk

In this section, we will focus on the communication protocol between the ODroid and the Pixhawk and the initial setup for the Wifi Module.

Important

Plug the FTDI module into the USB 3.0 port on the ODroid, which can be identified by the blue plastic inside the port. The WiFi module should be connected to the USB 2.0 port, which has a black plastic insert. Make sure each module is securely connected to its respective port.

Tip

If you have the breakout board for the ODroid that provides an extra USB port, plug the Wifi Module into that. You can purchase that here.

With the Wifi and the FTDI usb-uart module plugged in, run the following command:

# List all the attached USB peripherals
lsusb

Now, all the USB devices connected to the ODroid will be displayed.

Important

Make sure a "Realtek" wifi module device is present and a "FTDI" serial device is present.

Next, we need to check the status of the serial communication kernel headers. Use the following command to list all the kernel headers for the USB devices connected.

ls -al /dev/ttyUSB*

This should display something similar to /dev/ttyUSB0 on the terminal. The number 0 is usually assigned to serial communication modules. If it is not present, we need to debug further.

Tip

If /dev/ttyUSB0 is not present, check if /dev/ttyACM0 is present. If not, proceed to the next steps.

Note

Troubleshooting steps: Check if the usbserial and ftdi_sio system routines are running:

lsmod

If they aren't running, do the following

cd /etc/modules-load.d/
sudo nano modules.conf

Add the following lines to the file at the very bottom, save and exit

usbserial

Next, we need to create another .conf file

# Create the file /etc/modules-load.d/ftdi_sio.conf
sudo touch ftdi_sio.conf

# Open the configuration file in nano
sudo nano ftdi_sio.conf

Add the following line to the file, save and exit

ftdi_sio

Reboot the system, and the modules should load automatically. After rebooting, ensure that the modules are running by typing in lsmod in the terminal.

To ensure robust and consistent serial communication between the Pixhawk 6C and the ODroid M1S, we will create a custom symlink for the FTDI-based UART connection. This avoids issues caused by changing device names (like /dev/ttyUSB0, /dev/ttyUSB1, etc.) each time you plug or reboot your system.

Note

This setup assumes only one FTDI to USB module is used to connect the Pixhawk 6C to the ODroid M1S, as shown in the reference image. If you haven't already got the FTDI-USB module to the ODroid, connect it to the USB 3.0 port.

Tip

If you have multiple FTDI-to-USB cables, make sure the cable connecting Pixhawk to ODroid is plugged in last. This helps ensure the alias we create points to the correct device.

Next, we need to identify the FTDI-USB module. When connected, the FTDI module is typically assigned a device name like /dev/ttyUSB0. If you connect another FTDI, it may show up as /dev/ttyUSB1, and so on.

To identify your FTDI device’s vendor and product ID, run:

lsusb

You should see an output similar to:

Bus 005 Device 006: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC

From the above, we need to single out the following component IDs

  • idVendor: 0403
  • idProduct: 6001

Finally, we can proceed to create the custom system rule. Navigate to the udev rules directory:

cd /etc/udev/rules.d/

Create a new udev rule file:

sudo nano 99-pixhawk.rules

Add the following line to the file (replace the idVendor and idProduct values with your own if they differ):

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyPixhawk"

Reboot your ODroid for the changes to take effect:

sudo reboot

After rebooting, the Pixhawk FTDI interface will now consistently appear as:

/dev/ttyPixhawk

You can now reference this path in your scripts or serial communication setups instead of using /dev/ttyUSB0, ensuring stability and avoiding conflicts.

2.9 Setting up UXRCE-DDS

Clone the GitHub repository

git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git

Create the build folder

# Navigate into the cloned folder
cd Micro-XRCE-DDS-Agent

# Create the build folder
mkdir build && cd build

Compile

# Run Cmake
cmake ..

# Compile using all 4 cores present on the ODroid M1s
make -j$(nproc)

# Finally install
sudo make install

Once installation is complete, refresh the linkers

sudo ldconfig /usr/local/lib/

2.10 Installing Boost 1.86

Run the following commands to download and compile the latest version of Boost

# Update the apt repo
sudo apt-get update

Install some prerequisites

sudo apt install -y python3-dev autotools-dev libicu-dev libbz2-dev

Note

python3-dev: Development headers and static libraries needed for building Python modules or embedding Python in applications.

autotools-dev: Infrastructure scripts and files for GNU Autotools, used to build and configure software from source.

libicu-dev: Development files for ICU (International Components for Unicode), providing Unicode and locale support for C/C++ applications.

libbz2-dev: Development files and libraries for bzip2, enabling compression and decompression features in software.

Clone the repository in the home folder. Go to the home folder by typing in cd ~.

git clone --recursive --branch boost-1.86.0 https://github.com/boostorg/boost.git boost-1.86.0

Navigate into the folder using

cd boost-1.86.0

Start the bootstrap system

./bootstrap.sh --prefix=/usr/local

Compile and install

sudo ./b2 install -j$(nproc)

After the installation is done, refresh the linkers

sudo ldconfig

Verify the installation

grep "#define BOOST_LIB_VERSION" /usr/local/include/boost/version.hpp

This should show 1_86_0.

3. ROS2 Setup

Choosing a ROS 2 Distribution: Single vs. Multiple Installs

When using ROS 2, decide whether you want a single distribution or multiple versions installed. This choice affects how you set up your environment. The rest of the information in this subsection is only valid AFTER the installation of a ROS2 distro.

Warning

You can only source one ROS 2 distribution per terminal session or codebase. Sourcing multiple ROS 2 versions in the same terminal or codebase can cause conflicts and unexpected behavior.

Caution

The .bashrc file is a startup script that runs every time you open a new terminal. If you add a source command for a specific ROS 2 distribution in .bashrc, that version will load by default — and you won't be able to switch to a different ROS 2 distro unless you manually remove or comment out that line. Use this only if you're sticking to one ROS 2 version.

✅ Recommended: Use a Single ROS 2 Distribution

If you're only using one version (e.g., ROS 2 Galactic, recommended for ACSL-flightstacks), you can safely add the source command to your .bashrc after the installation steps covered in the sections below:

echo "source /opt/ros/galactic/setup.bash" >> ~/.bashrc
source ~/.bashrc

The above example sets up your terminal automatically for ROS 2 Galatic and will differ for Foxy and Humble.

⚠️ If Using Multiple Distributions

If you install more than one ROS 2 version (e.g., Galactic and Humble), do not add any sourcing to your .bashrc after installation. Instead, you will need to source the version you need manually each time:

# Please note that only one of the these source commands can be used at a time.

# ROS2 Foxy
source /opt/ros/foxy/setup.bash
# ROS2 Galactic
source /opt/ros/galactic/setup.bash
# ROS2 Humble
source <path-to-ros2-humble-build-directory>/install/local_setup.bash

Note

The sourcing command for ROS 2 Humble differs because Humble must be built from source on Ubuntu 20.04, whereas Foxy and Galactic can be installed using apt with precompiled binaries available for Ubuntu 20.04. This difference impacts how you install and manage the environment setup.

This avoids path conflicts and ensures you’re using the correct ROS 2 environment.

🛠️ Summary

Use Case .bashrc Sourcing Manual Sourcing Notes
Single ROS 2 distro ✅ Yes ❌ No Easier workflow
Multiple ROS 2 distros ❌ No ✅ Yes Avoid version mix-ups

Tip

Installing ROS2 Galatic is the best balance of ease of setup, performance and features for Ubuntu 20.04.

3.1 Setup ROS2 Foxy

Check and Set Locale (UTF-8)
Ensure your system is using a UTF-8 locale. On full Linux environments (non-Docker), this is typically already set. To verify:

locale

Make sure all values output use UTF-8. If not, or if you want to be sure, run the following:

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

Add the ROS2 Repository
Ensure the Universe repository is enabled:

sudo apt install software-properties-common
sudo add-apt-repository universe

Add the ROS2 GPG key and repository:

sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | \
sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Then, update your package list:

sudo apt update

Install ROS2

Choose ONE of the following installations based on the Ubuntu flavor you installed (GUI or server):

# Full desktop version (recommended for GUI Ubuntu 20.04 setup)
sudo apt install ros-foxy-desktop python3-argcomplete

# OR minimal base install (recommended for SERVER Ubuntu 20.04 setup)
sudo apt install ros-foxy-ros-base python3-argcomplete

Install ROS2 Development Tools

sudo apt install ros-dev-tools

Verify ROS2 Installation
Open two terminal instances on the ODroid (or SSH into it from your PC in two terminals).

In Terminal 1:

source /opt/ros/foxy/setup.bash
ros2 run demo_nodes_cpp talker

In Terminal 2:

source /opt/ros/foxy/setup.bash
ros2 run demo_nodes_py listener

You should see messages being published and received—this confirms that both the C++ and Python ROS2 nodes are working correctly.

Make Sourcing Persistent

Caution

Make it persistent ONLY if you are going to use ROS2 Foxy and not any other distro of ROS2 on the ODroid.

To avoid manually sourcing ROS2 every time, add it to your .bashrc:

echo "source /opt/ros/foxy/setup.bash" >> ~/.bashrc

Apply the change:

source ~/.bashrc

Check if ROS environment variables are properly set:

printenv | grep -i ROS

You should see variables like ROS_VERSION, ROS_DISTRO, and related paths listed.

Fix Deprecation Warnings (Optional but Recommended)

sudo apt-get install python3-testresources python3-pip
pip3 install setuptools==58.2.0
sudo reboot

3.2 Setup ROS2 Galactic

Check and Set Locale (UTF-8)
Ensure your system is using a UTF-8 locale. On full Linux environments (non-Docker), this is typically already set. To verify:

locale

Make sure all values output use UTF-8. If not, or if you want to be sure, run the following:

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

Add the ROS2 Repository
Ensure the Universe repository is enabled:

sudo apt install software-properties-common
sudo add-apt-repository universe

Add the ROS2 GPG key and repository:

sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Then, update your package list:

sudo apt update

Install ROS2

Choose ONE of the following installations based on the Ubuntu flavor you installed (GUI or server):

# Full desktop version (recommended for GUI Ubuntu 20.04 setup)
sudo apt install ros-galactic-desktop python3-argcomplete

# OR minimal base install (recommended for SERVER Ubuntu 20.04 setup)
sudo apt install ros-galactic-ros-base python3-argcomplete

Install ROS2 Development Tools

sudo apt install ros-dev-tools

Verify ROS2 Installation
Open two terminal instances on the ODroid (or SSH into it from your PC in two terminals).

In Terminal 1:

source /opt/ros/galactic/setup.bash
ros2 run demo_nodes_cpp talker

In Terminal 2:

source /opt/ros/galactic/setup.bash
ros2 run demo_nodes_py listener

You should see messages being published and received—this confirms that both the C++ and Python ROS2 nodes are working correctly.

Make Sourcing Persistent

Caution

Make it persistent ONLY if you are going to use ROS2 Galactic and not any other distro of ROS2 on the ODroid.

To avoid manually sourcing ROS2 every time, add it to your .bashrc:

echo "source /opt/ros/galactic/setup.bash" >> ~/.bashrc

Apply the change:

source ~/.bashrc

Check if ROS environment variables are properly set:

printenv | grep -i ROS

You should see variables like ROS_VERSION, ROS_DISTRO, and related paths listed.

Fix Deprecation Warnings (Optional but Recommended)

sudo apt-get install python3-testresources python3-pip
pip3 install setuptools==58.2.0
sudo reboot

3.3 Setup ROS2 Humble

Make sure you have a locale that supports UTF-8. If you are in a minimal environment (such as a Docker container), the locale may be something minimal like POSIX. We test with the following settings. However, it should be fine if you’re using a different UTF-8 supported locale.

locale  # check for UTF-8

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

locale  # verify settings

You will need to add the ROS 2 apt repository to your system. First, ensure that the Ubuntu Universe repository is enabled.

sudo apt install software-properties-common
sudo add-apt-repository universe

Now add the ROS 2 GPG key with apt.

# Update and install curl
sudo apt update && sudo apt install curl -y

# Add the GPS key
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg

Then, add the repository to your sources list.

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | 

sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Install common packages.

sudo apt update && sudo apt install -y \
python3-flake8-docstrings \
python3-pip \
python3-pytest-cov \
ros-dev-tools

Create a workspace and clone all repos:

# Create a directory in the home folder to build ROS2
mkdir -p ~/ros2_humble/src

cd ~/ros2_humble

# Import the repo
vcs import --input https://raw.githubusercontent.com/ros2/ros2/humble/ros2.repos src

ROS 2 packages are built on frequently updated Ubuntu systems. It is always recommended that you ensure your system is up to date before installing new packages.

Note

Make sure you are in the ~/ros2_humble root folder before running the following code.

sudo apt upgrade

sudo rosdep init
rosdep update

rosdep install --from-paths src --ignore-src -y --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers"

Tip

If you’re using a distribution that is based on Ubuntu (like Linux Mint) but does not identify itself as such, you’ll get an error message like Unsupported OS [mint]. In this case, append --os=ubuntu:jammy to the above command.

Note

This part of the installation is only applicable if you install the Server version of Ubuntu onto the ODroid M1s.

Before we build, we need to minimize the number of packages we are building; we do not need rqt, rviz, and a few of the examples associated with them as this will be on the drone. Create a script in the root folder by doing the following.

# Navigate to the root folder of the ros2 build repo
cd ~/ros2_humble/
 
# create and open script
touch minimize_ros2_packages.sh
nano minimize_ros2_packages.sh

Then copy and paste the code below into the script. Pay attention to your root directory if it is different.

#!/bin/bash

# ANSI color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
CYAN='\033[0;36m'
ORANGE='\033[0;33m'
NC='\033[0m' # No Color

# Define the root directory of your ROS 2 workspace
ROS2_DIR="/home/odroid/ros2_humble"

# Array of exact directories to create COLCON_IGNORE in
DIRECTORIES=(
    "$ROS2_DIR/src/ros2/rviz/rviz_assimp_vendor"
    "$ROS2_DIR/src/ros2/rviz/rviz_default_plugins"
    "$ROS2_DIR/src/ros2/rviz/rviz_rendering"
    "$ROS2_DIR/src/ros2/rviz/rviz_visual_testing_framework"
    "$ROS2_DIR/src/ros2/rviz/rviz2"
    "$ROS2_DIR/src/ros2/rviz/rviz_common"
    "$ROS2_DIR/src/ros2/rviz/rviz_ogre_vendor"
    "$ROS2_DIR/src/ros2/rviz/rviz_rendering_tests"
    "$ROS2_DIR/src/ros2/demos/action_tutorials"
    "$ROS2_DIR/src/ros2/demos/demo_nodes_cpp_native"
    "$ROS2_DIR/src/ros2/demos/intra_process_demo"
    "$ROS2_DIR/src/ros2/demos/logging_demo"
    "$ROS2_DIR/src/ros2/demos/quality_of_service_demo"
    "$ROS2_DIR/src/ros2/demos/composition"
    "$ROS2_DIR/src/ros2/demos/pendulum_control"
    "$ROS2_DIR/src/ros2/demos/topic_monitor"
    "$ROS2_DIR/src/ros2/demos/dummy_robot"
    "$ROS2_DIR/src/ros2/demos/pendulum_msgs"
    "$ROS2_DIR/src/ros2/demos/lifecycle"
    "$ROS2_DIR/src/ros2/demos/topic_statistics_demo"
    "$ROS2_DIR/src/ros2/demos/image_tools"
    "$ROS2_DIR/src/ros2/demos/lifecycle_py"
    "$ROS2_DIR/src/ros-visualization/interactive_markers"
    "$ROS2_DIR/src/ros-visualization/qt_gui_core"
    "$ROS2_DIR/src/ros-visualization/rqt_action"
    "$ROS2_DIR/src/ros-visualization/rqt_console"
    "$ROS2_DIR/src/ros-visualization/rqt_msg"
    "$ROS2_DIR/src/ros-visualization/rqt_publisher"
    "$ROS2_DIR/src/ros-visualization/rqt_reconfigure"
    "$ROS2_DIR/src/ros-visualization/rqt_shell"
    "$ROS2_DIR/src/ros-visualization/rqt_topic"
    "$ROS2_DIR/src/ros-visualization/python_qt_binding"
    "$ROS2_DIR/src/ros-visualization/rqt"
    "$ROS2_DIR/src/ros-visualization/rqt_bag"
    "$ROS2_DIR/src/ros-visualization/rqt_graph"
    "$ROS2_DIR/src/ros-visualization/rqt_plot"
    "$ROS2_DIR/src/ros-visualization/rqt_py_console"
    "$ROS2_DIR/src/ros-visualization/rqt_service_caller"
    "$ROS2_DIR/src/ros-visualization/rqt_srv"
    "$ROS2_DIR/src/ros-visualization/tango_icons_vendor"
    # Add more directories here as needed
)
 
# Function to ensure COLCON_IGNORE files exist and display messages
ensure_colcon_ignore() {
    local dir="$1"
    local ignore_file="$dir/COLCON_IGNORE"

    # Check if COLCON_IGNORE file exists
    if [ -f "$ignore_file" ]; then
        echo -e "${CYAN}COLCON_IGNORE already exists in ${dir}"
    else
        echo -e "${ORANGE}Creating COLCON_IGNORE in ${dir}"
        touch "$ignore_file"
    fi
}

# Function to count existing COLCON_IGNORE files
count_colcon_ignores() {
    local count=0

    # Loop through each directory in DIRECTORIES array
    for dir in "${DIRECTORIES[@]}"; do
        local ignore_file="$dir/COLCON_IGNORE"
        if [ -f "$ignore_file" ]; then
            ((count++))
        fi
    done

    echo "$count"
}

# Main script logic
# Loop through each directory in DIRECTORIES array and ensure COLCON_IGNORE
for dir in "${DIRECTORIES[@]}"; do
    ensure_colcon_ignore "$dir"
done

# Count existing COLCON_IGNORE files
num_colcon_ignores=$(count_colcon_ignores)
num_directories=${#DIRECTORIES[@]}

# Compare counts and output result
if [ "$num_colcon_ignores" -ge "$num_directories" ]; then
    echo -e "${GREEN}${num_colcon_ignores} COLCON_IGNORE files present for ${num_directories} directories specified. Packages minimized for compilation.${NC}"
else
    echo -e "${RED}Failed to minimize packages for compilation.${NC}"
fi

save it with ctrl + o and exit with ctrl + x. Make it executable by running

chmod +x minimize_ros2_packages.sh

and run it with

./minimze_ros2_packages.sh

If successful, the output should look something like this

Creating COLCON_IGNORE in /home/odroid/ros2_humble/src/ros2/rviz/rviz_assimp_vendor
.
.
.
.
Creating COLCON_IGNORE in /home/odroid/ros2_humble/src/ros-visualization/rqt_srv
.
.
40 COLCON_IGNORE files present for 40 directories specified. Packages minimized for compilation.

This should tell the compiler to ignore the packages we don't want in the next step. We can now build the code in the workspace

Please follow the instructions in the above note if you have the server version of Ubuntu installed. We can now proceed to build ros2 humble.

cd ~/ros2_humble && colcon build --symlink-install

Tip

if you are having trouble compiling all examples and this is preventing you from completing a successful build, you can use COLCON_IGNORE in the same manner as CATKIN_IGNORE to ignore the subtree or remove the folder from the workspace. Take, for instance, you would like to avoid installing the large OpenCV library. Well, then simply run touch COLCON_IGNORE in the cam2image demo directory to leave it out of the build process.

Now, we don’t want to have to source the setup file every time we open a new shell. We can add the command to our shell startup script, which makes it easier while we SSH in:

Caution

Make it persistent ONLY if you are going to use ROS2 Humble and not any other distro of ROS2 on the ODroid.

echo "source ~/ros2_humble/install/local_setup.bash" >> ~/.bashrc

After running the above command, verify the installation

printenv | grep ROS

you should get an output similar to this

ROS_VERSION=2
ROS_PYTHON_VERSION=3
ROS_LOCALHOST_ONLY=0
ROS_DISTRO=humble    

Now, the bash shell will automatically source the packages for ROS2 Humble and we do not have to source it every time we try to do anything with ROS2 on the ODroid M1s.

4. Set up VIO Packages

Kernel 5.15.x is the last supported kernel version for the librealsense2 developer sdk. Therefore, before installation, run the following code in the terminal to verify that the kernel is the correct version.

uname -r

Note

your output should look something like 5.15.0-051500-generic, the first digit and the following two digits after the period are what matter.

If you followed the instructions in section 1, the Ubuntu version installed should be 20.04 with the kernel version 5.15. If that is not the case, please follow the instructions in troubleshooting 1.2 and change the kernel version to 5.15

Caution

The latest stable version of the development environment that supports the Intel RealSense T265 is librealsense2 SDK v2.53.1. Other versions are not compatible, so please ensure you download this specific version.

Before proceeding further, make sure the system is up to date and that we are on the latest stable version of the 15.5 kernel. Update the system and upgrade any necessary packages.

sudo apt-get -y update && sudo apt-get -y upgrade

Now we need to clone the exact version of the firmware that supports both the Intel Realsense T265 and Intel Realsense D435i cameras that are used in the lab.

# go to the home directory
cd 

# download recursively the v2.53.1 branch 
git clone --branch v2.53.1 --recurse-submodules https://github.com/IntelRealSense/librealsense.git

# go to the librealsense directory
cd librealsense

# check if this is the right tag
git describe --tags

Next, we need to set up the Linux backend for the development environment. Install the essential packages required to build the librealsense binaries and the necessary kernel modules:

sudo apt-get install libssl-dev libusb-1.0-0-dev libudev-dev pkg-config libgtk-3-dev

Now install some distribution-specific packages:

sudo apt-get install libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev at

Now, run the Intel RealSense permissions script from the root directory.

Caution

Make sure all the cameras are disconnected before proceeding.

# go to the root directory
cd ~/librealsense

# Run the permissions script
sudo ./scripts/setup_udev_rules.sh

Tip

One can always remove permissions by running:

sudo ./scripts/setup_udev_rules.sh --uninstall

Now build the binaries with cmake

# go to librealsense root
cd ~/librealsense

# build and cd into build directory
mkdir build && cd build

# run cmake
cmake ../ -DFORCE_RSUSB_BACKEND=TRUE

Recompile and install Librealsense binaries:

sudo make uninstall && make clean && make -j8 && sudo make install

Note

The shared object will be installed in /usr/local/lib, header files in /usr/local/include. The binary demos, tutorials, and test files will be copied into /usr/local/bin.

Run the following command to refresh the object linker in Linux

sudo ldconfig

Clone this wiki locally