I wanted a remote control car that I could control via WiFI, in case I wanted to program it to do stuff over the network. I couldn’t find a commercial product I liked that did this in August 2022, so I made this.
This car was built to be controlled via this controller. However, the control architecture was built to be generic, so it could support different types of controllers. I wanted the ability to record commands sent to it via Joystick, and then replay them so I could program routes or patterns of movement for the vehicle in an intuitive way. The ability to program another Arduino to act as a controller to send a series of commands for fixed routes by the press of a button or on a timer was also desired.
Lessons Learned the Hard Way:
The plastic cover of the car had a surprisingly powerful effect on connection reliability. The maximum reliable range appeared to be only 5 meters/16 feet, even with a direct line of sight to the router.
Arduino Uno WiFi Rev 2 does not offer good support for external antennas.
To get a better reliable range it was necessary to completely refactor the design to something that offered good support for external antennas.
It would have been wise to read the specs for my motor controller more carefully, which also offered encoder support. This was unfortunately not realized until after I wrote code to use the encoder shield.
It would have been smarter to use a differential steering library instead of writing this functionality myself. I didn’t do it because it was easy, I did it because I thought it would be easy when I started.
I enjoy drinking Soylent, the popular nutritional replacement drink. It is efficient in terms of money, nutrition, and time to create/consume/clean. I do not enjoy the process of manually making Soylent, which I found myself doing dozens of times per year. This sparked the motivation to automate the process, ideally with an Arduino, which was a new technology I was interested in exploring in 2017.
General Operation:
The moving platform moves the container between the powder dispensing and mixing stations about once per minute, about 15 times, in a process that completes in about 20 minutes. Once the container is filled with water and the process starts, it can proceed unattended and finishes automatically.
Source Code:
The source code for this project is available on Github:
Notice: I deliberately did not invest a lot of time in writing Clean Code because I wanted to focus on other aspects of the project. Be advised Uncle Bob would not look kindly on the above code.
1. Fine Soylent powder had a tendency to clump unexpectedly within the dispenser, causing it to stop dispensing entirely on occasion until I knocked the dispenser unit to break it free. This was eventually resolved by installing a strong mechanical vibrator to automatically clear the clumps. This in turn caused issues keeping the screws in place, as the additional vibration had a tendency to knock them out of their sockets over time.
2. Lining up the blender head and the container reliably and accurately proved surprisingly challenging. It became necessary to put screws on the moving platform so that the container would fall into a specific place each time I placed it there. Lining up the moving platform and blender correctly took many iterations, and I eventually strapped on lasers to the blender head so it was easier to visualize exactly where it would drop down.
3. Dispensing too much powder into the container in a single pass could easily create a situation where the blades became completely covered in powder, and additional dunking/spinning would not clear them. This is the primary motivation for so many passes between powder dispensing and mixing stations.
4. Originally I wanted a station in the middle that would dispense water into the container in the right amount automatically during the start of the process. This was later cut because getting the process to work at all proved more time-consuming than originally expected, partly due to the above lessons as well as a general under-scoping of the project at the outset.
Like everyone’s monolith, Earnest’s is complex and was once a little bit out of control. Earnest’s monolith is involved in all major aspects of the business. It is responsible for accepting loan applications from clients, making a decision for each of those applicants, and even supporting clients after loans have been signed. These wide-ranging responsibilities mean that the monolith is really five different applications in one body of code.
Over 100 developers have contributed to its codebase since inception. The complexity of so many revisions and updates made it difficult to set up and maintain. Beyond standard npm libraries, almost a dozen different dependencies from a database to a mock SQS queue to a Hashicorp Vault instance needed to be set up correctly for it to work completely on a developer’s computer. Engineering teams had come to expect that getting this application set up on a new computer would take at least a week, and would require the assistance of multiple developers who had been at the company long enough to acquire the necessary tribal knowledge.
As an engineering team, Earnest needed a way to ensure everyone had a consistent local environment. It needed to be quick and easy to set up. Finally, the local environment should have greater parity with the CI, staging, and production environments. In order to accomplish these objectives, I turned to Docker, Docker Compose, and The Go Script pattern.
I started the solution by addressing the shared node_modules folder between all five applications. All application containers shared the same node_modules folder inside a Docker volume. Any of these containers could be started in any order and update the npm dependencies. Therefore it became necessary to ensure only one container could write to node_modules at a time.
While there are many ways to control the start up order of Docker containers, I chose to create a bash script that would lock file descriptors at runtime and then executed this script in the entrypoint of each container. After this script ran, it would invoke the application’s process and the application container would be usable by the developer. An application container’s Docker Compose file looks like this:
Here is the entrypoint script for each application container.
So: the first time a user starts up the application containers, one of them will grab the lock and install the dependencies. The rest of the containers will wait for it to finish, see that the dependencies are valid before turning the control over to the grunt startup tasks. Dependencies are automatically checked and updated, but subsequent start ups will occur quickly and without calls to “npm install” until the dependencies change.
In the event of a container shutdown, networking failure, or Docker daemon shutdown, the lock on the file descriptor is released automatically. Developers can restart the Docker containers and continue with their workflow to recover from this unexpected failure.
In addition to the container startup synchronization system, there is a Docker image that contains the correct versions of node, npm, and other programs. Docker Compose links the application containers, a Postgres container, a mock Amazon SQS queue, and other supporting containers.
I have implemented The Go Script pattern as the last piece of the puzzle to make setting up the application, starting it, and running the tests one-step commands. This pattern is used by almost every project at Earnest, and it’s implementation in this project brings it in line with the rest of Earnest’s tooling. Developers new to the project can become productive quickly, and all developers can keep their focus on high-level goals instead of low-level implementation details.
Accomplishing these goals was time-consuming and difficult, but worth it. Team morale improved as a longstanding pain point in the daily life of Earnest software developers was eliminated. Earnest’s software developers reported that this tool increased their efficiency by an average of 32.5% when working on the monolith. On an average workday, this tool is used around 200 times by the engineering team.
This was originally a post on Earnest’s engineering blog, but has been cross posted to my blog as I am the original author and did the work described in the post.
3D model + render of a Gretsch guitar my father plays created off of a few reference images. Created Winter/Spring 2012. Maya 2012 used for modeling/rendering, Blender for UV mapping, GIMP for textures.