Main goal of this challenge is to show that uProtocol can hold up to its promises to provide a well defined, working and protocol agnostic service mesh which can be implemented from the cloud to microcontrollers running a safety certified RTOS here ThreadX. To show this we leverage uProtocol to implement a small but interesting case of a remote service namely "Honk&Flash". The command, a uMessage is triggered on an Android app written in Rust and travels via the MQTT uProtocol transport down to a microcontroller where the protobuf message gets deserialized and triggers the actual honk&flash via blinking in LED.
We also implemented the communication in the other way and for this communication we choose to implement a crash detection. Here the technological choice of threadX makes a lot of sense since it provided us with the necessary abstractions to have the crash detection running with higher priority than the other task hence fulfilling strict timing requirents. After detecting a crash a uMessage is constructed, serialized via protobuf and send over the wire via MQTT. Here it is picked up by the mobile phone and notifies the user that a crash has happend. This is also shown on the uC screen.
From a technical standpoint this challenge proves that it is feasible to implement uProtocol ie. uMessage (de)serialization within a uC / RTOS setting and that it is possible to bridge a lot of different technologies and provide a usable abstraction to develop software for the SDV.
Checkout the demo folder for pictures and videos of our demo.
The application initializes a wifi connection, connects to an MQTT broker and subscribes to the topic
"d/Cloud/3039/1/8001"
which is a topic message from a cloud directed at this device will be received. This conforms to the uProtocol spec (https://github.com/eclipse-uprotocol/up-spec). This message will trigger an Honk&Flash operation where the LED of the microcontroller will blink. The application will also consistently monitor acceleration and will send uMessage to the cloud via the topic
"d/MXChip/5BA0/1/8001"
indicating to the backend that a crash has happend. The backend will then forward this to the Mobile App. In a real scenario this could be an alternative communication path for the emergency call which should be much faster than traditionell SMS.
Deployment view
The application uses ThreadX and spwawns 3 threads. A fourth (Logging thread) is optional.
- A networking thread
Networking thread sets up the Wifi and MQTT connection. An event flag
status = tx_event_flags_create(&mqtt_app_flag, "my app event");
is used to signal two conditions to the networking thread. It either shows that
a) A message is ready to be reveived b) A message is ready to be send
Upon message receiption the command is send to the Honk&Flash thread via the mqtt queue which is created as follows:
UINT status = tx_queue_create(&queue_mqtt, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE * sizeof(ULONG));
- Crash detection thread
Constantly measures the accelerometer data and signals a crash via the event_flag to the networking thread. This thread runs with an elevated priority since it has to meet certain timing constraints.
- Honk & Flash Thread
Listens on the mqtt_queue and starts the honk and flash process when it receives the message:
queue_status = tx_queue_receive(&queue_mqtt, &received_message, TX_WAIT_FOREVER);
To work on the microcontroller protobuf fields with a variable length like BYTES, STRING etc. we limit those types to a length of 128 bytes.
- Shortcomings / Workarounds*
To overcome the problem that the MQTT client only support version 3.1 uMessages are serialized via protobuf to their wire format and send as the payload of the MQTT message. This does not directly conform to the uTransport but it would be a compatibel addition to it. We also patched the https://github.com/eclipse-uprotocol/up-transport-mqtt5-rust/ to support the transportation as payload.
A backend application written in Rust which leverages a patched version of https://github.com/eclipse-uprotocol/up-transport-mqtt5-rust/ to subscribe to the MQTT broker and converts the msg payloads to uMessages (protobuf) and dispatches them to the mobile application. It will also send the honk&flash commands downstream.
Mobile application which talks to the backend via HTTP(S) and sends uProtocol (Honk&Flash) messages to the device. It also receives and displays the crash detection events.
We try to test as often as possible and work in small increments. For the Microcontroller part testing is mostly done via manual testing. Test cases are described in the Microcontroller section. The Backend (Rust) part uses unit tests which are implemented and executed with the embedded rust unit test runner via
cargo test
Testcases:
- Startup and check UART log for WIFI messages
- Switch through the menu
- Toggle Ambient light and check that displayed colour matches LED color
- Check accelerometer dashboard to reflect small accelerationn
- Via mobile phone trigger Honk&Flash operation and check that light is flashing
- Trigger crash mode and check via Mobile Phone that the crash is indicated. Restart controller.
Testcases can be found in [#test] section of main.rs
Eclipse ThreadX and Eclipse ThreadX NetX Duo are included as submodules.
When cloning, you must specify the --recurse-submodules
option to get the code for the submodules. If you forget this option, just run the following commands in the root folder of your clone.
git submodule init
git submodule update --recursive
Theoretically, any recent laptop running Windows 11, Linux, or MacOS should do.
The board code was developed with MacOS running on Mac M3 chips.
In terms of tooling, all you need to work on the challenge is CMake, Ninja, and a suitable C compiler. Naturally, having Git installed could help as well. ;-)
The source code for ThreadX and related modules is very portable and compliant with all "required" and "mandatory" rules of MISRA-C:2004 and MISRA C:2012. Most modern C compilers should be able to compile it. The official build pipelines rely on Arm's embedded GNU toolchain.
If you wish to generate the proto C bindings you can use nanopb (https://github.com/nanopb/nanopb). There is a short description on what has to be done in the file
protobuf_generation.txt
Below are instructions to install the tools.
OSX We assume that brew is used as a package manager. To install the ARM toolchain use:
brew install --cask gcc-arm-embedded
to use command line flashing also install openocd and for debugging we used minicom.
Ubuntu
apt install ninja-build cmake
Then, download and install Arm's embedded GNU toolchain, available at https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
The following will download and unpack version 13.3.rel1 of the software to /opt
.
wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
sudo tar xJf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz -C /opt
To test, you can run the following commands:
export PATH=$PATH:/opt/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin
arm-none-eabi-gcc --version
Windows
winget install --id=Arm.GnuArmEmbeddedToolchain -e
winget install --id=Ninja-build.Ninja -e
winget install --id=Kitware.CMake -e
To compile the application, simply execute the relevant script found in the MXChip/AZ3166/scripts
folder. You can use either build.sh or build_fresh.sh where the latter one does a full recompile.
To flash the application onto the device you can use
flash.sh
To connect the board to a WiFi network, edit the following constants found in cloud_config.h
:
HOSTNAME
WIFI_SSID
WIFI_PASSWORD
Make sure to select an appropriate value for WIFI_MODE
as well.
If the WiFi is properly configured, you will get the output below at application startup:
Initializing WiFi MAC address: C8:93:46:87:88:D1 SUCCESS: WiFi initialized Connecting WiFi