Arduino UNO Q Elf Detector Series - Part 1: Blinking an LED

Photo of Raul Muñoz

Posted on Dec 8, 2025 by Raul Muñoz

9 min read

Welcome back to the Arduino UNO Q Elf Detector Series. I’m happy to report that the Arduino UNO Q survived the first night! I left a decoy microcontroller on the workbench, an old 8-bit board with bent pins, and sure enough, it was gone this morning. But the Arduino UNO Q remained safe in my hidden drawer.

In Part 0: Introduction, we successfully unboxed the device and connected it to the FoundriesFactory™ cloud using the fioup agent. Now that our device is online and listening for updates, we need to establish a perimeter. It is time to deploy our first application: a web-based LED controller that demonstrates the Arduino UNO Q dual-core architecture.

In this part, we will demonstrate how to use the power of the FoundriesFactory platform CI/CD. We will write our code on our host machine, push it to our Factory's Git repository, and watch as the platform automatically builds the application and deploys it to the Arduino UNO Q over the air.

Prerequisites

Before proceeding, you must have completed Part 0: Board Setup and Factory Onboarding.

To follow this guide, you should have:

  • An Arduino UNO Q powered and connected to the internet.
  • A FoundriesFactory Account with a created Factory.
  • The fioup agent installed and the device registered.
  • Git installed on your host computer.

The Hardware Architecture

Before we begin, let's recap some of the features of the hardware we will be taking advantage of, the Arduino UNO Q features two processing cores:

  1. Qualcomm Dragonwing™ MPU: Runs Linux® (Debian). This will host our Python web server.
  2. STM32U5 (MCU): A real-time microcontroller. This will handle the physical LED control.

The Bridge:

We will use the Arduino Bridge to connect these two worlds. Our Linux application will communicate with the MCU over a Unix socket, allowing a web page to trigger real-time hardware events.

Step 1: Create an API Token

To interact with your Factory's source code securely, you need an API token.

Go to your Factory https://app.foundries.io/factories/<factory-name>/ and click on your avatar on the right top corner. Select Setting and API Token.

  1. Navigate to your Factory dashboard at https://app.foundries.io/factories/\<factory-name>/.
  2. Click on your Avatar in the top right corner and select Settings, then API Token.
  3. Click Create New Token.
  4. Crucial Step: Ensure you select the source:read-update scope. This permission allows you to push code to the repository.
  5. Give it a description (e.g., "Dev Machine") and an expiration date.
  6. Click Generate and copy your token immediately.
Generate API form

Step 2: Clone Your Factory Repository

When you created your Factory, the platform automatically provisioned a private Git repository for you. This is where your source code lives.

  1. Navigate to the Source tab in your FoundriesFactory dashboard and copy the clone URL.
  2. On your host machine, clone the repository using the token you just created:
host:~$ export FACTORY=<FACTORY-NAME>host:~$ export TOKEN=<TOKEN>
host:~$ git clone https://[email protected]/factories/$FACTORY/containers.git
host:~$ cd containers/

You will see a folder structure ready for your applications.

We will create a new directory for our project inside containers/

Step 3: Understanding the Application

We are about to push a small demo app to the device. It is a web-based dashboard that gives you direct control over the board's hardware. By accessing a website hosted locally on the device, you can toggle the LEDs on and off with a click, instantly translating a browser command into a physical action.

Before we deploy it, let's look at how it works. The application consists of three main components inside the led-controller directory:

1. The Dockerfile

This file tells the Factory CI how to build the app. Crucially, it installs arduino-cli and openocd. This allows the container to compile and update the MCU firmware inside the application every time it starts.

2. The MCU Firmware (sketch.ino)

This C++ code runs on the STM32U5. It registers the toggle_led function so the Linux side can call it using Bridge.provide().

3. The Web Server (main.py)

This Python script runs on the Linux MPU. It serves a web page and uses Bridge.call("toggle_led") to send commands to the MCU.

Step 4: Deploying the Code

To save you from manually creating every file, we have prepared a demo repository. We will pull the code from our demo repo and push it to your Factory.

Inside your containers.git folder, run the following commands:

  1. Add the demo remote:
host:~$ git remote add arduino-demo https://github.com/foundriesio/containers.git
host:~$ git fetch arduino-demo arduino-demo-25Q4
  1. Checkout the application folder:
host:~$ git checkout arduino-demo/arduino-demo-25Q4 -- arduino-led-webui
  1. Push to your Factory
host:~$ git commit -m "Adding Arduino LED WebUI demo"
host:~$ git remote remove arduino-demo
host:~$ git push

What Just Happened?

When you pushed the code, FoundriesFactory automatically triggered a new Job in the CI/CD pipeline. It is now building your application in the cloud.

Target building

Step 5: Updating the Device

By default, the fioup daemon on your device is not set to automatically apply every update immediately. For this tutorial, we will trigger the update manually to see the process in action.

  1. Wait for the build to finish in your FoundriesFactory dashboard.
  2. On your device (via ADB or SSH), check for the update:
device:~$ fioup check
  1. You should see output indicating a new version is available
device:~$ fioup check

- - - - Output Example - - - New version available: arm64-linux-2. Please run 'fioup update' to get it.
  1. Apply the update:
device:~$ fioup update
  1. The device will verify the target, download the new Docker images, and install them.
device:~$ fioup update

- - - - Output Example - - -
[1/6] Checking ... new update from -1 [] to 2 [arduino-led-webui]
[2/6] Initializing ... fetch size: 647.4 MiB, 23 blobs; add: [arduino-led-webui], remove: [], sync: [], update: []
[3/6] Fetching ...
         100%  [=========================]  647.4 MiB / 647.4 MiB | 22/23 blobs | Cur:       0 B/s | Avg: 11.56 MiB/s | Time: 56s | ETA: 12:30:35 (done)

[4/6] Stopping ... done
[5/6] Installing ...
        Installing app hub.foundries.io/advent2025/arduino-led-webui@sha256:d5404ecc3861c90251
        Loading image hub.foundries.io/advent2025/arduino-led-webui@sha256:8e71ead7eebc75aff4
        	b89cf3e  100%  [=========================]
        	94edf93  100%  [=========================]
        	474700f  100%  [=========================]
        	d688e50  100%  [=========================]
        	5d85fb9  100%  [=========================]
        	2183403  100%  [=========================]
        	47f8309  100%  [=========================]
        	10cb5b8  100%  [=========================]
        	163462e  100%  [=========================]
        	4484946  100%  [=========================]
        	d44138e  100%  [=========================]
        	14d414e  100%  [=========================]
        	ff1329b  100%  [=========================]
        	047ee53  100%  [=========================]
        	8426cf4  100%  [=========================]
        	4f4fb70  100%  [=========================]
        Image loaded: hub.foundries.io/advent2025/arduino-led-webui@sha256:8e71ead7eebc75aff4
        Already exists: hub.foundries.io/advent2025/arduino-led-webui@sha256:8e71ead7eebc75aff4
      Done
[6/6] Starting ...
	starting arduino-led-webui --> hub.foundries.io/advent2025/arduino-led-webui@sha256:d5404ecc3861c90251 ... done
      Done

You can also see your device online on FoundriesFactory and what application it is running on: Visit https://app.foundries.io/factories/\<Factory-Name>/devices/

Devices

Step 6: Testing the Result

Once fioup reports "Done," your application is running.

1. Verify the Container is Running:

Check the device logs to ensure the container started successfully:

device:~$ docker ps -a

You should see arduino-led-webui with the status Up.

- - - - Output Example - - -
CONTAINER ID   IMAGE                                           COMMAND           CREATED          STATUS          PORTS     NAMES
afa73bfb63a6   hub.foundries.io/advent2025/arduino-led-webui   "/app/start.sh"   47 seconds ago   Up 45 seconds             arduino-led-webui-arduino-led-webui-1

2. Access the Web Interface:

You can access the web interface on any computer on the same network as your Arduino UNO Q. Simply open your browser and navigate to http://<device-IP>:8000

Web dashboard
  1. Click the buttons on the interface.

If successful, the LED on your Arduino UNO Q will blink!

You have just triggered a hardware interrupt on a microcontroller via a web request processed by a Linux container deployed entirely via the cloud.

Step 7: Automating Fleet Management

To prepare for the next demonstration, we will switch from manual updates to automatic fleet management. We will also use the cloud dashboard to remove the current application, returning the device to a clean slate.

1. Update the Factory Configuration

First, we tell the platform that we no longer want this application running on our device.

  1. Navigate to your Devices page: https://app.foundries.io/factories/<FACTORY-NAME>/devices/.

  2. Select your device from the list.

    Device manager
  3. Click Update tags & apps

  4. Remove the App: Move arduino-led-webui from the right (Selected) list back to the left (Available) list

    Tag manager
  5. Click Apply

2. Configure the fioup Daemon

Currently, the update is pending in the cloud because our device agent (fioup) is not running in automatic mode. We need to enable it.

To make the device check for updates frequently (for demo purposes), we will first set the polling interval to 20 seconds.

Connect to the device’s terminal and run:

device:~$ sudo sed -i '/\[uptane\]/a polling_seconds = "20"' /var/sota/sota.toml

Now enable and start the fioup daemon

device:~$ sudo systemctl enable fioupdevice:~$ sudo systemctl start fioup

3. Watch the Update in Real-Time

Now that the daemon is active, it will automatically detect the configuration change we made in Step 1. Follow the logs to watch it, remove the application:

device:~$ journalctl -f -u fioup

You will see the agent initialize, detect the removal, and stop the container:

[2/6] Initializing ... remove: [arduino-led-webui]
...
[6/6] Starting ... Done

4. Verify Cleanup Finally, confirm that the container is no longer running on the device:

device:~$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Your device is now ready to automatically pull whatever we build next.

Conclusion

In this part, we shifted our workflow from local hacking to Cloud CI/CD.

By configuring the fioup daemon, you now have a powerful Docker Container manager running directly on your Arduino UNO Q. This setup enables you to continuously build, integrate, and deliver containerized applications using FoundriesFactory. Instead of manually transferring files or flashing boards one by one, you simply push code to your repository, and the platform handles the orchestration, building, and secure delivery to your device.

In Part 2 of this series, we will utilize this fleet management capability to deploy a more sophisticated application: an Interactive LED Matrix Controller.

Resources

For a deeper dive, explore the repository to gain a better understanding of the code:

Related posts