When you do not have a Robot Car, you would think there are plenty of things to do with it, like see what’s going on around the house without moving from your couch, scare your cat, annoy your significant other, etc. Well, when I finally got one, I did all that the first day, and then put it on a shelf.
This article does not intend to be a product review but I have to give it to the guys at UCTRONICS. Their Robot Car Kit for Raspberry PI is great for anyone interested in getting started with robotics. From the packaging, through the instructions, to the general quality of the materials, UCTRONICS does a great job. You would have as much fun putting it together as you will have using it, and it works the first time.
Back from the shelf…
I picked up my robot car, powered it up, ssh’d to the Raspberry PI, and start looking around. I found what they use to stream video, MJPEG streamer, and a WebSocket server.
With a little research, I found the C language code repository on Github and started reading it.
I have written code in C and C++ language for many years and what I found would have not survived a peer review. Lack of good structure, poor documentation, inconsistent coding style, commented out code,… it just looked like a first prototype, but it works.
It was then that I decided if I want to enhance, modify, or add features to the robot car, I was not going to do it using that codebase. So, I started from scratch.
I also wanted to be able to control the robot car from the internet, not just within my home network.
Main features like manual control, obstacle avoidance, and line tracking shall work similarly to the original
I will not support the UCTRONICS mobile app as it would not be useful outside my home network, and instead, write a basic web app to control the robot car using any browser.
Finally, it has to be open-source, of course. Here is the link to my Github repository.
Several choices had to be made in terms of server and reactive web frameworks, node modules, client-server protocol, etc.
Since my requirement was to be able to control the robot car securely from the internet I decided to use NGINX as a web server and reverse proxy.
Finding the right node.js module to control the Raspberry PI GPIO was not a trivial task, as I needed to be able to access some GPIO ports that are not available through the filesystem. The performance was also a factor. After trying several modules I decided to use pigpio. It has all the functionality I needed. The pigpio node module is a wrapper to the pigpio C library.
Finally, I chose Vue.js to implement a simple, rudimentary web application for testing and demonstration purposes, that would communicate to the backend server using secure WebSocket through NGINX.
So, here is how the big picture looks:
Besides the camera module accessed by the MJPEG streamer, there are basically 6 pieces of hardware to control.
Each one is represented by a Node.js module that abstracts the GPIO writing and reading and provides simple APIs, for instance, the echoSensor.js does all the hardware setup and initialization and just provides an API to get the distance to the obstacle in front.
All those modules also provide an API to register a listener for hardware status changes.
Here is an example from echoSensor.js:
On top of these modules, there is the carRobot.js module. This module is responsible for instantiating and initialize all the hardware wrappers, and register the listeners for hardware status changes as shown in the snippet below:
This module is also responsible for processing command from the client to move the robot, turn lights on and off, beeping, orienting the camera module, and implements automatic driving functions like obstacle avoidance and line tracking.
Finally, the server.js module sets up the WebSocket connections with the clients and implements a rudimentary protocol consisting of commands from the client to the server and events (feedback) from the server to the client.
Finding how the Robot Car kit uses the Raspberry PI GPIO was done mostly by reading the original C code, but also some trial and error iterations.
Whoever has experience with Raspberry PI knows that there are at least three ways to number a GPIO pin: physical, Broadcom, and WiringPi.
Here is the list of GPIO pins for each piece of hardware using the Broadcom denomination:
Very simple: just write ‘1’ to GPIO 26 to beep.
Camera Servo Motors
The camera can be moved using servo motors both horizontally and vertically. pigpio node.js module makes it very easy by providing a servoWrite API.
Just right anything between 500 and 2500 to move it from one extreme to the other while writing 1500 to both servos will basically center the camera.
The horizontal servo motor is controlled by GPIO 7 and GPIO 6 controls the vertical one.
The line tracker function uses three IR reflective switches. By reading and comparing the values (‘high’ or ‘low’) of these switches the robot knows whether is center on the line or deviating to the left or right and apply the corresponding corrections.
GPIO 0, 2, and 3 are used for the left, center, and right IR reflective switches respectively.
To measure the distance to an object in front, an echo sensor needs to emit a signal and read the reflection on that object. Therefore two GPIO pins are needed. The trigger (which emits the signal) is controlled by GPIO 26 while the echo (which reads the reflected signal) is read on GPIO 23.
The kit uses an addressable LED strip called ws281x. To render the LED light and colors I used another Node.js module that wraps pigpio for this specific purpose, the rpi-ws281x-native. It cannot be easier to render any color by providing a combination of 3 integers which are the intensity (0–255) of the blue, red, and green. The GPIO used for this is GPIO 1
The DC motors used to drive the robot car cannot be powered directly by the Raspberry PI GPIO since they consume more current than the RPI GPIO can provide. That’s why additional hardware is needed to provide enough power. The robot car kit uses a couple of half-H bridges to power the wheels DC motors.
To actually power 4 DC motors we would need 8 GPIOs but instead, the kit uses a shift register. In digital circuits, a shift register is a cascade of flip flops, sharing the same clock, in which the output of each flip-flop is connected to the “data” input of the next flip-flop in the chain, resulting in a circuit that shifts by one position the “bit array” stored in it, “shifting in” the data present at its input and ‘shifting out’ the last bit in the array, at each transition of the clock input. So, a cascade of 8 flip-flops would produce an 8 bits output. The 8 bits array would have 128 combinations where the DC motors would not run (0–0, and 1–1), and 128 combinations where one or more DC motors would run (forward or backward). Of course, not all combinations make sense. Here is the list of the combinations that I needed to implement the robot car movements.
By using a shift register we only need three GPIOs, one to enter the data (1 bit at a time), one clock to shift the data through the cascade of flip-flops, and finally, one GPIO to latch the output. The GPIO pin numbers are 27 for “Data”, 28 for “Clock” and 29 for “Latch”.
The speed of the motors is controlled with 4 GPIOs used as output PWM. The pins are GPIO 21, 22, 23, and 24.
Serving the Web application
As I mentioned before I wanted to be able to access the robot car server from the internet. The easiest way to achieve that was to get a free DNS service from DuckDNS, by choosing a subdomain like ‘example.duckdns.org’ and point it to my ISP IP address.
Next, I used Let’s encrypt to obtain the TLS signed certificate. Let’s Encrypt is a nonprofit certificate authority.
Finally, I set up my router to forward traffic on port 443 to my Raspberry Pi.
All the steps to set up this project and all the code, for both the server and the web application are available on my GitHub repository.
Just remember although this project was built to run the UCTRONICS Robot Car kit, it can as well run any other with similar hardware just by changing the GPIO numbers as needed.
I hope you enjoyed the article.