ExoMax is an adaptation of ExoMy (https://github.com/esa-prl/ExoMy ) using an alternative software stack as well as a pan and tilt camera instead of the fixed one from the original.

Architecture 2

It uses motion for video stream generation, a Kotlin/JVM Rover Software running on a Raspberry Pi, a universal Swift App that can run on AppleTV, iPad and Mac. Communication is realized using mosquitto. Video is taken directly from the local motion instance as MJPEG and resized on the fly. Then every image is published via MQTT. This is not very efficient but it makes the whole setup extremely flexible in terms of where to put each component.

Architecture 1

Here, the mosquitto is located on the rover directly, it can also be placed somewhere else. By doing this, full remote control via internet can be achieved.

Architecture 2

Rover Software

The Rover Software is an IntelliJ IDEA Project. You can directly open it. Before building it there are a few steps to be taken first.

Basic Raspberry Pi setup

Install a Raspberrypi OS headless and enable ssh. Connect to the Raspi using Ethernet and setup Wifi using sudo raspi-config. Also rename your Raspberry to Exomax. This ist the hostname that is used throughout the whole project.

We need a small set of software to be installed:

PackageWhy
OpenJDK8Our Rover Software runs on this
MotionUsed for streaming video
WiringpiTo interface with I2C using Pi4J
mosquittoThe MQTT broker
sudo apt-get install openjdk-8-jre-headless motion wiringpi mosquitto
sudo systemctl enable mosquitto.service
sudo systemctl enable motion
sudo systemctl start motion

Configure Motion

Edit /etc/motion/motion.conf and modify the following lines

# Start in daemon (background) mode and release terminal (default: off)
daemon on

# Level of log messages [1..9] (EMG, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). (default: 6 / NTC)
log_level 1

# Image width (pixels). Valid range: Camera dependent, default: 320
width 800

# Image height (pixels). Valid range: Camera dependent, default: 240
height 600

# Maximum number of frames to be captured per second.
# Valid range: 2-100. Default: 100 (almost no limit).
framerate 15

# Output pictures with only the pixels moving object (ghost images) (default: off)
output_debug_pictures off

# Use ffmpeg to encode videos of motion (default: off)
ffmpeg_output_movies off

# Make automated snapshot every N seconds (default: 0 = disabled)
snapshot_interval 0

# Output frames at 1 fps when no motion is detected and increase to the
# rate given by stream_maxrate when motion is detected (default: off)
stream_motion off

# The mini-http server listens to this port for requests (default: 0 = disabled)
stream_port 8081

# Maximum framerate for stream streams (default: 1)
stream_maxrate 15

# Restrict stream connections to localhost only (default: on)
stream_localhost off

Restart the motion service to apply the changes.

sudo systemctl restart motion

Try it using a Webbrowser by accessing http://exomax:8081. Check the /var/lib/motion folder if no images or videos are stored. This can destroy your Raspberry. If there are files other than the log file then you might need to adapt the config file.

Install Pi4J

I installed Pi4J omn the Pi using the recommended way by curl -sSL https://pi4j.com/install | sudo bash, However, It might be advisable to check with the instructions first https://pi4j.com/1.2/install.html

Build The Rover Software

Open the Rover folder in IntelliJ IDEA. Then everything is set up and you are good to build it.

Build the Artifact and use copyToRover.sh in order to clean unused certificates and copy the jar to the Raspberry.

It should be possible to run IDEA on the Pi itself but this project is build with the intention to build all software on a Mac (especially the Controller) and copied over to the Pi.

Servo configuration

Currently there is no automated process to configure the servos but this is rather straight forward. You just need to know what servo is connected to which pin. In ServoController.kt the servos are configured as constants. they are named after their position on the rover. frd is front right drive, mls is middle left steering and so on. There is always a drive servo as well as a steering servo.
When there is no camera attached to your rover, just ignore the camera servos.

Setup the Rover Software and make it start up on system boot.

Create a new file at /etc/systemd/system/exomax.service

With the following content:

[Unit]
Description=Exomax Rover Software
Wants=network.target
After=network.target

[Service]
Type=simple
Restart=always
ExecStart=/bin/bash -c "java -jar /home/pi/Rover.jar"
Restart=always

[Install]
WantedBy=multi-user.target

Enable autostart for the rover software using sudo systemctl enable exomax.service.

Now the Rover should be good to go. Reboot. If you build the Rover software on the pi directly, either move the resulting jar to the home folder or change the service file accordingly.

Controller Software

Controller

Open the Controller Software in XCode and run it on any device.

There is a version for iOS, optimized for landscape view on iPhone and iPad. There is also a version for macOS which should run on all Macs (including M1) and one for AppleTV. Exomax is controlled using a game controller in all cases. It is possible to connect up to 4 controllers, all of them are then assigned the same functionality and can be used simultaneously. On touchscreen devices, the buttons for switching the driving modes are also active. Communication to MQTT is established every time the App enters foreground mode and disconnected with the inactive state. The controller assignment is as follows:

Controller

ButtonExoMax BehaviorKeyboard
Directional PadFixed camera positionsQEVB
AAckermann driving modeR
BSpot Turn driving modeF
XCrabbing driving modeT
YCenter CameraC
Left shoulderIncrease speed by 10 and lock speed controlW
Left triggerStop and release speed controlSpace
Right shoulderDecrease speed by 10 and lock speed controlS
Right triggerStop and release speed controlSpace
Left thumbstickDrive controlAD
Right thumbstickFree Camera ControlArrows