Intracto, the company that I work for, has been a Gold sponsor of PHPBenelux for a few years now. As a gold sponsor, you’re granted a sponsor booth in the sponsor area of the conference. To attract more attention, most sponsors offer some sort of contest/raffle where conference attendees can win a prize. The theme of this years conference was “retro”, so we were brainstorming about original ideas to organize a competition with a prize to win.
The first idea that sprung to mind, were retro arcade-style games (pac-man, centipede, space invaders, …) but we assumed this would be an obvious choice that other sponsors might also come up with. Then we thought about “retro” gaming consoles like Nintendo with Mario Kart. Again, a bit to obvious for our liking. So finally we came up with Carrera slot car racing. It’s more physical than a video game, and we all had one when we were young. Although they’re still produced, they idea alone made us nostalgic.
To spice it up, I came up with the idea to device an automatic time registration system, so that we could do time-trials as a contest. I reckoned it should be doable using an Arduino and some basic components, and use PHP to read the output from the Arduino over the USB serial line.
The first design I came up with, was to use infrared break beam sensors. Basically you have an IR light and a sensor. When something (like a car) goes between the light source and sensor, the light beam is broken which the Arduino can detect. Initial proof of concept showed that the sensor was fast and accurate, I just needed to find a way to mount everything on the tracks.
The first idea was to put the lights in the middle of the track, and the sensors on the side. I dusted off my 3D-printer, made a design in Sketchup and printed the mounts for the sensors. The problem was that at that point I did have a piece of track, but didn’t have any cars. When the cars eventually showed up, it turned out that the IR lights in the middle of the track were 2 mm too wide. This would definitely cause problems when the cars would pass at speed.
The second idea was to place both the light and the sensor at the side of the track and put a reflective surface like a thin mirror in the middle of the track. I designed and printed new mounts, took a piece of cardboard and covered it with a highly reflective piece of fabric I got from a yellow safety vest I stole from a “gilet jaune” (ok, that last part was a lie).
The idea worked, but wasn’t accurate enough. A small center piece allowed the beam from the other side of the track to be picked up, and making the piece larger made it harder to keep it securely in place and upright. Since the thin part would be in the middle of the track, there would always be a chance that a car would crash into it.
I had one other solution in mind with the IR break beam, but that would make the build a lot more difficult. So at that point, I started looking in other directions. A colleague of mine suggested using a hall effect sensor to detect the change in magnetic field, triggered by the magnet that is on the bottom of the car. The thin sensor would be mounted below the track, making it a clean solution. Unfortunately, the sensor wasn’t sensitive enough to pick up the passing car as the magnet is mounted directly above the metal power rails in the tracks, so the sensor had to be positioned next to that.
Next up was the light dependent resistor (LDR). The idea was simple. Make a small hole in the track to catch the ambient light. When the car passed, the light would drop and the difference in resistance could be detected by the Arduino. The concept worked on paper, but not in reality. The LDR was too slow. When the car was not above the LDR, the Arduino would read a value of 500. When the car was stationary above the LDR, that would drop to 10. But when the car was passing at speed, it would drop to anything ranging from 100 to 400, depending on the speed of the car. To make it worse; holding my hand 50 cm above the track, casting even the lightest of shadow over the LDR, would block enough light to make the reading drop to 200-300 or less.
So finally, I was forced to pursue the more difficult approach with the IR break beam sensor I though of earlier. Building a bridge over the tracks, place the IR lights on the bridge facing downwards and put the IR sensor in the track so that the car would pass over the sensor. Based on earlier findings, the idea was good but there were a few practical problems that needed to be solved.
The biggest problem was that the sensor is 1 cm thick, where the track is only 5-6 mm. This would mean that the track had to be raised 5 mm in order to fit the sensor under it. This wasn’t ideal, as that could make the track unstable and wobbly. I’m not sure how exactly it happened, but at some point I rather coincidentally removed the back of one of the sensors. It revealed a simple and small board with the sensor mounted on it. All just a few mm thick in all directions. I made a small hole in the track, fitted the sensor bulb through it and hot-glued the board to the bottom of the track. It fitted perfectly. The bulb would raise above the track, but low enough so that the cars would not hit it when passing over it.
The next problem was the bridge. Given the fact that I just have a simple Prusa style 3D-printer, I’m not able to print complex structure that fan out in different dimensions. I wanted to secure the posts of the bridge under the track using a U-shape hook, and add small platforms on the top section where the IR lights could be mounted on.
I eventually divided the bridge into 3 parts (2 posts and the upper bar) that I could actually print on my Prusa and then firmly attach the pieces to each other using slots and anchors I provisioned in the parts. After a few iterations, the design was quite solid and did exactly what I wanted. I then hot-glued the lights to the bar. Add some soldering and wiring and my contraption was almost ready.
Since we didn’t know the exact layout of our sponsor booth, I wanted to be able to put more distance between the laptop hooked up to the Arduino, and the actual track piece with the sensors that I crafted. To accomplish this, I ended up using 2 RJ-45 jacks. One hooked up to the Arduino, the other one hooked up to the sensors. We could then just use the length of network cable that suited us best, without having to re-solder any components.
Inside the Arduino
On the Arduino side, I wrote a script that would detect when the beam was broken and when not. When it transitioned from unbroken to broken, I would write the track number along with the milliseconds it took to complete the track out to the serial port. I decided it was best to have the Arduino do the actual timing, as it would rule out any lag/delays the PHP script might encounter when reading and processing the times. I also experimented with having the Arduino keep track of more lap times (the idea was that people would have to complete 10 laps in the time-trials), but since the Arduino is limited in both memory and flexibility, it was probably better to let the laptop do the hard work and have the Arduino just focus on the current lap. It did have a check to see if the lap was longer than 1 second, just in case to weed out any false positives.
The PHP application
The PHP application was broken into 2 parts. A worker process that would get the data from the Arduino and a web application that would provide a scoreboard and a registration page for the racers.
The worker process was a rather simple script that would read the input from a stream. On Linux, reading from the serial port of the Arduino is easy as you can just open /dev/ttyUSB0 as you would with a regular file. It also allowed me to fake input quite easily for when I did not have the Arduino at hand, as I could switch to other streams.
The worker would read the track and lap time from the Arduino, and write it to a sqlite database. The database structure was very minimalistic, maybe a little too minimalistic. A “racers” table and a “laps” table, where the “key” between the tables was the combination of a track number and a timestamp range that was bound by the timestamp field of the next racer. It was good for what it had to do at first, but as features were added along the way it would have benefited from a more robust design. Time constraints were the main driver for not fixing that. I’ll keep that for v2 😉
If the track was in race mode, it would first check to see if they already completed their 10 laps. When the racer completed lap 10, the final time was calculated and stored in the racers table so that I didn’t need to recalculate that every time I needed that (which wasn’t easy due to the database structure).
On the front-end side, the application has a registration page for the racers where you need select track 1 or 2 and enter a name. Not specifying a name would put the track in practice mode.
The scoreboard was a page that would display 2 columns with 10 lap times, either the exact 10 lap times when in race mode, or the 10 most recent lap times when in practice mode. The fastest lap for each track would be highlighted, although we decided not to do anything with that (we choose the overall fasted race as a winner, not the fastest lap).
My initial design was a vintage late nineties, begin nillies table-like layout including some animated gifs and a marquee. It doesn’t get any more retro than that, does it? Fortunately, a colleague of mine who actually know something about front-end gave it a well needed face lift and added elements/information about our company and the vacancies we are trying to fill, yet keeping the gifs and the marquee.
Times would be read out using ajax requests every few seconds. This results in a seemingly lag when the car passes the sensors, as it can take up to 2 seconds before the screen was updated. Once more, time constraints prevented me from doing this another way. Websockets are also for v2.
So, how did it all perform on D-day? Perfectly, if I say so myself! Over 6000 laps were driven on our 6.4 M long track. That’s just shy under 39 KM. Given the 1:43 scale, that would be a real-world equivalent of 1.650 KM. Both the Arduino setup as the PHP application didn’t fail a single time. Nor did the track or the cars! And above all, it was fun as hell!
Hi there! My name is Tom. I’m a Linux System Administrator and PHP Developer. My job is to keep PHP websites running as smooth as possible. Being both on the ‘dev’ and ‘ops’ side gives me a broad skillset across the entire stack.