In this blog series I discuss the creation of Laser Drift, a system that allows you to remotely control any Carrera Digital slot cars by emulating the infrared wireless controllers for between one and four players in realtime.
Feel free to take a look at my promo video or the Github repo before you read.
A couple of months ago I was in a brainstorming meeting with the Creative Director, the Head of Strategy and an assortment of other tech people. This is a volatile melting pot of skills that in the past has produced many amazing things (and the odd Frankenstein). Our client had tasked us with "doing something awesome with one of those BCI devices". For those unaware, Brain-Computer Interface devices measure electrical activity in the brain and provide an SDK for performing computations based on simple thoughts, emotions and physical gestures.
These open briefs are generally great, but it can be difficult to come up with a viable idea that satisfies everyones concept of what constitutes awesomeness and (God forbid) fits within the proposed budget.
We eventually settled upon the idea of brain powered slot car racing, in which a user would think certain thoughts like "push" in order to make the cars move. So, in a nutshell, two to four players would race against each other and the first player to n laps would win. We felt this would work well because (as those of us old enough to remember already know) turning is automatic in slot car systems. Users had only to move forward and the rest would be be taken care of.
This idea also satisfied a few other prerequisites - most importantly it would allow users to interact with the physical world. The client, perhaps correctly, felt that it would be more impressive if the BCI device was able to move physical objects opposed to digital ones.
To me, this idea actually involved two separate projects. First:
- PC -> Slot Cars: We needed to create software to accurately control up to four players on a slot car set using simple, language-agnostic commands
- BCI -> PC: We needed to create software to listen for mental commands like "push" and "pull" and then translate them into those simple commands I mentioned above
In this series I will discuss the former as it is by far the most interesting and challenging of the two - even though "mind control" may sound cooler in a press release.
The project manifested itself in three phases. In this post I will discuss the first two:
In a separate post I will discuss the creation of the solution itself: Laser Drift.
The only experience my colleagues and I had with slot cars were distant childhood memories of analogue cars zipping around single-lane tracks. And so the first task at hand was to find out what a state of the art slot car set looked like in 2017.
Wow! They've really come a long way.
In the old days, each car needed to occupy it's own lane. And if any two cars got in the same lane, they would always travel at exactly the same speed. They were fun, but relatively monotonous toys.
But that was the past! For in about 2004, the major manufacturers started introducing digital systems in which each car could be uniquely identified (even if they occupied the same lane). So now players can freely change lanes, simulate oil spills, have pit stops, configure braking performance and even represent real-world concepts like fuel levels and tyre blowouts. All of this was going to allow for a whole new dimension of badassery in the end result.
The next point of call was to decide was which slot car manufacturer we would go with. As it turns out, there are two main players in the slot car world: Scalextric and Carrera. The general sentiment among enthusiasts is that Scalextric is for the older, hobbyist demographic whilst Carrera is for the younger crowd who just want to have some fun. But both options are of a very high quality.
After some looking around, I found that Carrera offered a bluetooth interface called the AppConnect that would allow one to program car speeds, set fuel levels and perform various other tasks. One Thomas Kemmer had also released Python bindings into the underlying protocol, whch meant I could easily whip up a python program to control the cars. I was starting to think this whole thing may be much easier than I initially thought...
Oh, ignorance is bliss.
My elation was quickly extinguished when I realised that by "programming car speeds", Carrera had meant that the bluetooth interface can be used to pre-program cars with individual top speeds before a race. It's of no use during a race! Alas, we still ordered a new (and pretty damned expensive!) Carrera Digital 132 GT Championship Wireless+ set and the bluebooth adapter regardless. This particular set came with wireless controllers, which communicated with the control unit on the track via infrared (atleast, that is what I had read on the official forums).
At this point I realised that there was no official way of using a PC to control each car in realtime. And so we hit the drawing board.
The first idea was to physically press the accelerator on the controllers using something like a linear actuator. The controllers themselves would be housed in a chassis of some kind and we'd use a hobbyist microcontroller like an Arduino to press down on each controller when desired. If you watch the first 20 seconds of this video it should be obvious how this may work.
This would have the benefit of not requiring us to open the hood of the Carrera set but simply to emulate the pressing of a thumb. Although it did require a whole bunch of additional hardware for each controller and some specialised skills that no one on the project professed to have. It also didn't give us the accuracy we were really after as the actuators we found tended to actuate very slowly.
The next idea was to reverse engineer the controllers infrared signals. We could emulate the entire array of controllers as long as we could send the right data packets at the right time. I mean, the controllers are basically like really simple TV remotes, right? This would require some hardware aswell, but atleast there would be no soldering involved.
I did some additional research online and I was able to find some brialliant work by Stephan Heß in which he showed the basic contents of the infrared packets sent by the controllers. This meant that we already had some of the reverse engineering completed for us. And I already knew that cheap USB-based IR transceivers like this one from Irdroid were readily available.
So it was decided: We would use an infrared transceiver to emulate the data sent by the controllers to the control unit. One simple device would act as all of the controllers.
One additional curve ball was thrown my way when I took a closer look at the controllers we had. They were not the 38khz IR controllers I had read about but actually 2.4ghz RF! This was going to make communication a whole lot more difficult. As it turns out, the IR controllers were decommissioned in 2013 and replaced with the superior RF models. Luckily, it seemed that the new control units still supported the older controllers. So I got en ebay and bought the older gear that I wanted.
The IR transceiver I purchased was the Irdroid. Interestingly, it's handmade in Bulgaria in repurposed SD card readers. The reason I chose this device was because it's supported out of the box by the predominant infrared tool suite for Linux environments, Lirc. LIRC is a package that allows you to decode and send infrared signals of many (but not all) commonly used remote controls. A full introduction to it is beyond the scope of this article, but it's sufficient to just know that lirc gives you the ability to send and receive IR blasts in a Linux environment.
An important part of reverse engineering is to take small steps. Try to get a general idea of how the device works before trying to reach the end goal. For me, the first port of call was to see what the information coming from the controllers looked like. To do this, I used a low-level tool that lirc provides called mode2. Mode2 simply listens for raw IR data and prints it to
Let me explain what IR data looks like.
Infrared devices typically encode their message to the receiver via a method called Pulse Code Modulation (PCM) in which an IR-emitting LED is switched on and off rapidly. When the transmitter is "off", we call it a space. And when the transmitter is "on", we call it a pulse. There are a few common methods for encoding binary into pulses and spaces, but all involve transitioning between said pulses and spaces.
In my case, I expected to see a random looking series of pulses and spaces being printed to
STDOUT. But I got nothing. No matter what I did, I could not get any output from the controller. I spent the next hour troubleshooting why the Carrera control unit could pick up on the IR data but I couldn't. It wasn't until I pointed my IR transceiver at the control unit itself that I realised there was more to the story.
The control unit (well, technically the IR device on the unit) was actually sending out the following signals every ~70 milliseconds:
pulse 800 space 700 pulse 550
The numbers are microseconds. What we are seeing here is a pulse of 800μs, a space of 700μs and then a pulse of 550μs. This was repeated ad infinitum.
As it turns out, the controllers only respond when they are instructed to by the control unit. Pressing the button does not actually cause an IR transmission. It just updates some state on the microcontroller in the controller itself. This, I assume, is important for synchronising the controllers so that their transmissions do not get tangled on the receiving end.
Cool. So the next thing I did was transmit the syncing blast from my transceiver to the controller and see what it returned. This is what I got back:
pulse 300 space 1000 pulse 250 space 250 pulse 250 space 250 pulse 500 space 250 pulse 250 space 500 pulse 250 space 250 pulse 250 space 250 pulse 250
At the suggestion of a friend, I wrote a little script to visualise this with a step graph where a pulse is high and a space is low.
I performed this test a bunch of times. I could see that the transmission repeated every ~60 ms. And it always started with a pulse of 300 followed by a space of either 750 or 1000. This was definitely the start bit, which told the control unit that the blast was coming from a controller and that the rest of the transmission was the payload.
Following the start bit was a series of pulses and spaces that were always either
After some additional experimentation and research I presumed Carrera were using Manchester Coding to encode their data packets. In Manchester Coding, a bit is represented as a transition from either low-to-high or high-to-low. Specifically,
1 is encoded as a pulse of Nμs followed by a space of Nμs. And
0 is encoded as a space of Nμs followed by a pulse of Nμs.
As we can see, in our case
N = 250. So each bit required exactly 500μs. Summing the numbers together (minus the start bit) we get
4000 / 500 = 8. So the data packet consisted of a start bit followed by eight bits (one byte) of data.
Consulting the work of Stephan Heß, which by this point had saved me many hours, I was able to confirm the structure of the data packet sent by each controller. Each controller sent the start bit, followed by two bits representing the player, four bits for the speed and one bit to indicate whether the lane-change button was pressed. Wait - that's only seven bits!?
As it turns out, there was also a trailing bit that seemed to be used to represent additional players. I assume it was added to the end for backwards compatibility with older models. This also explained why there was no end bit on the data word - it allowed for future expansion of the payload whilst maintaining backwards compatibility.
I've drawn the payload contents below.
As an example, let's suppose we were player #3 travelling at speed 7 with the lane change button pressed. Our 8 bits would look something like this:
To encode these bits into pulses and spaces using Manchested Coding we can first add the start bit:
pulse 300 space 750
And then we start adding in each bit one by one. Remember that bits in Manchested Coding are represented as a transition. So let's add the 1 (a pulse followed by space):
pulse 300 space 750 pulse 250 space 250
So far so good. Let's add the next bit, a 0 (a space followed by a pulse):
pulse 300 space 750 pulse 250 space 250 space 250 pulse 250
Uh-oh! Now we have two spaces in a row. We can fix this by simply merging them into one, longer space:
pulse 300 space 750 pulse 250 space 500 pulse 250
Cool. Every time we see a
0->1 or a
1->0, we are going to merge one end with the other to produce a pulse or a space of 500μs. Now let's add in the rest of the bits:
pulse 300 space 750 pulse 250 # 1 space 500 # 0 pulse 250 space 250 # 0 pulse 500 # 1 space 250 pulse 250 # 1 space 250 pulse 250 # 1 space 250 pulse 250 # 1 space 500 # 0 pulse 250
And we're done!
Here is another representation:
And when we visualise it as a step graph, the data packet is very similar to the one I showed above. You should be able to see the difference between the 250μs and 500μs bursts.
And with this, the reverse engineering project was complete. From here I was able to use lirc to listen for the syncing blast from the Carrera control unit and respond with some hardcoded data packet.
After I solved some minor timing issues I was elated when I saw the car move! It was proof enough for me, at the very least, that the idea was not totally bonkers.
Below is a video of the first time I got the car to move without a physical controller. In this video I am ignoring all syncing efforts from the IR tower on the Carrera set. I am just blasting the same message over and over and hoping the timing is correct. This explains why the car only moves in little bursts.
Wow, this post got long...
I am going to leave the details of the end-result to another blog post. For now, you can check out Laser Drift on Github or watch the promo video I embedded at the top of this article.
As we all know being a competent software developer or engineer means continuously learning throughout your career. It’s great! That makes this profession exciting and allows to escape everyday routine at work. In fact, it’s not a job anymore - it’s a lifestyle 👩💻👨💻