Menu

andipanda.dev

CaRadio: FastCarPlay, LIVI, and Restoring Carlinkit Connectivity

In previous articles, I described how I managed to get CarPlay running on a Raspberry Pi and the role the Carlinkit USB dongle plays in the system.

But between the first successful FastCarPlay launch and having a working CaRadio, there was another story I haven't really talked about yet.

It involves choosing the software foundation, experimenting with another project, and an unexpected problem that left CarPlay unable to connect at all.

Why I Chose SDL3

In my early experiments with a car head unit, I used Qt and C++.

Qt is a powerful tool for building full-featured graphical applications. But over time, I started wondering how much of it was really necessary for a device that only needed to perform a relatively small set of tasks.

When I found FastCarPlay in the spring of 2026, one of its advantages for me was that it was written in C++ using SDL.

I started learning about SDL3 and realized that I liked this approach much more.

A graphical application doesn't necessarily need X11 and the entire Qt framework. It's possible to work with a much smaller set of components and build the interface yourself.

Of course, that doesn't mean SDL3 is better than Qt in every respect. They're tools designed for different purposes.

But for my head unit, SDL3 was closer to what I wanted: a relatively small application without unnecessary software layers.

Besides, FastCarPlay already used SDL, so moving toward my own implementation based on the same technology seemed like a logical choice.

First Launch of FastCarPlay

After looking through the source code, I built FastCarPlay and tried running it with my Carlinkit USB dongle.

And CarPlay actually worked.

Not everything was perfect, though.

I can no longer remember every problem I encountered during those first attempts. I only remember that the system wasn't behaving quite as expected, so I started looking for possible causes.

During that search, I came across another open-source project — LIVI.

I decided to give it a try as well.

Discovering LIVI

LIVI turned out to be quite a large project compared with FastCarPlay.

It was also written in a programming language I wasn't familiar with.

That doesn't mean it was a bad project. Quite the opposite: after installing LIVI, it worked correctly on my setup.

But I didn't want to spend time understanding a large codebase written in an unfamiliar language and then build my own head unit on top of it.

I prefer C++, and when it comes to DIY devices like this, I try to follow one principle: the simpler, the more reliable.

Of course, simplicity alone doesn't guarantee anything.

But from my own experience, I've noticed that the more you complicate a design in its early stages, the more potential sources of problems you introduce.

Additional dependencies, services, and software layers all need to be configured, maintained, and considered when troubleshooting.

That's why I wanted to keep FastCarPlay as the foundation and gradually adapt it to my needs.

However, after experimenting with LIVI, an unexpected problem appeared.

When CarPlay Stopped Connecting

After running LIVI, I went back to FastCarPlay.

And discovered that it could no longer detect my phone at all.

Previously, CarPlay had at least launched, even if there were some problems. Now it wouldn't connect at all.

I never established the exact cause.

I suspected that LIVI might have changed some setting or internal state in the Carlinkit dongle, causing the previous connection procedure to stop working.

But that remained only a hypothesis.

What I did know was that one project still worked correctly with the adapter, while the other could no longer establish a connection.

That difference became the starting point for further investigation.

Restoring Carlinkit Initialization

Since LIVI was still working, I decided to use it as a reference.

The goal wasn't to rewrite FastCarPlay in another language or copy the entire LIVI codebase into it.

I needed to understand how the USB dongle initialization procedure differed between the two projects.

I began studying the relevant parts of their implementations and comparing the sequence of communication with Carlinkit.

Gradually, I found differences that could affect how the connection was established.

Based on those observations, I modified the initialization procedure in FastCarPlay.

After the changes, the phone started connecting again.

This time, though, the result was better than during my first experiments: I managed to achieve a stable connection and reliable CarPlay operation.

That was an important milestone for me.

Until then, I had mostly been trying to run and adapt existing solutions. Now I had to investigate how the application communicated with the USB dongle and find the cause of the incompatibility.

That doesn't mean I developed the CarPlay protocol from scratch.

I used existing open-source implementations, compared their behavior, and fixed the part of the adapter communication that was preventing my system from working properly.

Why I Still Chose FastCarPlay

After these experiments, I had two working starting points.

LIVI demonstrated that the adapter was capable of connecting and operating correctly. Its implementation also helped me understand the initialization problem.

But I still preferred FastCarPlay as the foundation for further development.

There were several reasons.

First of all, C++. It's a language I've worked with for a long time, and I'm comfortable using it to develop and maintain my own projects.

The second reason was SDL3. I liked the possibility of building a graphical application without relying on X11 and Qt.

And the third was the size and complexity of the project.

I didn't want to turn a DIY head unit into a large software system before its basic functions were even working.

My approach is fairly simple: first make the minimum necessary set of components work reliably, then gradually add new features.

Of course, as CaRadio developed, the application inevitably became more complex.

But I wanted that complexity to come from actual requirements rather than being introduced in advance.

From Experiments to My Own CaRadio

Once I had solved the connection problem, I finally had a working software foundation I could build on.

FastCarPlay handled communication with Carlinkit, SDL3 was suitable for building my own interface, and C++ allowed me to develop the application in an environment I was familiar with.

It still wasn't a finished car head unit.

I needed to combine CarPlay with the other CaRadio functions, work out how the application would be controlled, and adapt it for everyday use in a car.

But one of the most frustrating problems had already been solved: connecting the phone was no longer an unpredictable experiment.

Only then could I focus on developing my own head unit instead of constantly trying to get CarPlay to launch at all.

What's Next?

This experience reminded me once again why I prefer not to complicate my own projects unnecessarily.

Sometimes a small program you understand well is more convenient than a large ready-made solution with lots of features.

At the same time, without LIVI, it might have taken me considerably longer to figure out the Carlinkit connection problem.

So both projects played a part in CaRadio's development.

FastCarPlay became the software foundation, while LIVI helped me identify and fix the problem that was holding me back.

In future articles, I'll cover other parts of CaRadio and the challenges I had to solve to turn a working CarPlay setup into a fully functional car head unit.