Introduction: ESP32-Powered Tabletop Kinetic Sand Drawing Robot
In 2020, back when I had just started my sophomore year of high school, I built a very basic sand plotter robot that drew patterns in sand. Three years later, I wanted to use the skills that I have taught myself to revisit the design in order to be able to take it to college with me to Purdue in a couple of weeks! You can read more about the original robot here
The original inspiration came when I saw an Instagram ad for the SANDSARA Indiegogo campaign back in late 2019/early 2020.
This redesign has been in the making for the past six months. I recently wrote it up on my main blog here, however it was more of a showcase. I will attempt to explain in detail how to construct it, but feel free to leave a comment or contact me if you would like any more information :)
Supplies
3D Printer
ESP32 DevKit V1
2x TMC2208 -or- TMC2209 stepper drivers
2x mini nema 17 pancake steppers
2x hall effect endstop (generic ones will work fine)
TSL2561 light sensor
Panel mount USB-C and DC power jack
Custom PCB
4 inch lazy susan bearing
Assorted countersunk M3 screws
SK6812 RGBW led strip (1m) I used 144 leds/m
M3 heat set inserts (4.5mm OD)
Assorted 2,3,4,5 pin JST connectors and SMT sockets.
400MM GT2 timing belt w/ pulleys.
Step 1: Fusion 360
I designed this kinetic sand robot to be extremely slim, and with everything printed, it is under 3in tall! There are a lot of overlapping parts, mostly around where the large theta gear attaches to the base plate. The entire robot was designed from scratch in Fusion 360, and I've shared them online through Fusion Team (here)
Yes, Fusion 360 does say that this is version 55 of the mechanism... I spent longer than I want to admit during this phase of design :)
I started by sketching the rough shape of the enclosure, and then split it up along the large overhang to ensure printability. I then built the robot baseplate, modeled the lazy susan bearing, and the central gear stackup.
I've attached both a cutaway view and an exploded view of the robot assembly for more detail into how it goes together.
Step 2: 3D Printing!
I printed everything in black Polymaker ABS using 30-50% gyroid infill depending on the part. Usually, the internal components got 50%, and I just printed the enclosure with enough shells to make it solid. I'd estimate that I used almost all of the 3kgs of filament I bought for this project. I did a few printer upgrades during this project as well :p
PLA would've fared better in terms of less warping, however I wanted the structural properties of ABS.
I've attached a photo of me test-fitting the linear axis and it's guide rails.
Step 3: Robot Assembly
I added 4-pin JST connectors to the stepper motors, a 5-pin connector to the TSL2561 light sensor, and 3-pins to both magnetic endstops and the LED strip.
I then installed the theta stepper motor with countersunk M3 screws to the robot base plate. Then, I put the GT2 pulley on the motor, and screwed the lazy susan bearing into both the large central theta gear and the lower base plate using M3 screws. The bearing snaps right into place and shouldn't wobble. In the Fusion assembly, there is an optional spacer that can be used if it does. The linear axis and guide rails screws directly into the top of the theta gear with some heat-set inserts.
Step 4: PCB Design
I designed a very basic breakout board for all of the components using the standard version of EasyEDA and had it manufactured with a black soldermask.
I actually ended up changing the name to "Tranquil" when I was writing the web interface, I just never really wanted to re-order the boards just for some text.
The design is available here
Step 5: PCB Assembly
I simply soldered down the motor drivers, ESP32, and the JST connectors. I added an optional SD card slot that is compatible with MPN#47352-1001 (Mouser) I hand-soldered the connector with a fine point tip and some no-clean flux.
Step 6: Connect It All Up
I installed one of the magnetic endstop sensors right into the tiny hole offset from the center to sense when the theta gear arrived at a known position
The fully assembled robot base plate simply slides into the center of bottom half of the enclosure.
Step 7: Assemble Enclosure
Place top half of enclosure on bottom half. Screw down with heat set inserts & 12 10mm M3 screws.
Step 8: Add Sand
I took some generic craft felt I had at home and spray glued it to the bottom of the sand bed to make it super quiet. Then, I just dumped some Home Depot sand and spread it around with a butter knife.
Step 9: Lights! Camera? No, Just Lights..
I stuck the LED strip on the sides of the sand bed with some double sided foam tape, and ran the 3 wires (5V data and ground) through the tiny slit on the side, down to the main control board.
Step 10: Compiling the Web Interface
The web interface was written from scratch with a mix of TypeScript and Vue 3. State management is done with Pinia and data fetching is done with a set of Axios request interceptors.
The GitHub repository is here.
Simply clone and build the project to continue,
You can build the project on a *nix machine by running (change command for equivalent on Windows machine)
yarn && yarn build
Save the generated build files that are output in the ./dist directory.
Step 11: Compiling the Device Firmware
The firmware has been re-written from almost the ground up and has been in the works for at least a year now. It's a normal PlatformIO project and can be downloaded from here. Drag the built web assets to this directory and PlatformIO will automatically move them to the ESP32's SPIFFS partition.
The patterns featured are not provided and can be downloaded from the open internet. The web interface simply lists what patterns are installed on the device.
Step 12: Configuring Device
Once the firmware is installed, the device will setup it's own access point named "Tranquil" where all initial configuration is done. You can connect it to your Wi-Fi, setup OTA updates, and even (in my case) set it up as a WireGuard client. (see next step)
Step 13: Wireguard Client
You can go to the settings tab of the device and insert the relevant keypair and endpoint information for the robot to join a remote WireGuard subnet.
I make use of this as the college that I will be attending in the fall implements client isolation on their network, and without this, I would have no way to control the robot.
Step 14: Enjoy!
Thank you so much for reading! If you want to build your own, but are stuck somewhere, please let me know in the comments and I will definitely try my best to help!
I've now built two sand drawing robots and have gained so much experience in designing for printability and ensuring that the firmware that I write is stable!
You can see more of my projects on my blog: https://vigue.me, or follow me on GitHub @acvigue
![3D Printing Student Design Challenge](https://content.instructables.com/FP2/NF0A/LGZ4Z8NL/FP2NF0ALGZ4Z8NL.jpg?auto=webp&crop=1240%2C600&frame=1&width=320)
Grand Prize in the
3D Printing Student Design Challenge
36 Comments
Question 20 days ago
Hi Aiden, I am finally able to make the almost everything work. I can home the setup, control the LEDs and change the settings. However, I am having issue with the patterns. I have also created the TranquilAPI server on Cloudflare using R2 and D1 (most probably not the correct way) and added the server address in the frontend code. When I click on the patterns tab, I see a dialog for sign in asking for email and password. I am not sure what to put here. I tried my Cloudflare credentials, but it does not work. It will be kind of you if you can help me in setting up the Cloudflare server (maybe some instructions).
Also, is it possible to put the .thr files on the sd card without using the TranquilAPI server.
Answer 20 days ago
I also had certain issues, mainly with the SD card. Now, I'm waiting for the PCB to test everything properly. On the breadboard, everything was working except for the SD card. In the TranquilVue code within main.ts, you should search all the way to the end for 'window.authInProgress' and set it to true. This will stop asking for authentication. Give it a try and let me know so that I can figure out what else I need to deal with. Additionally, try putting the *.thr files directly on the SD card to see if it recognizes them for execution. Sorry for my English, I don't handle it very well
Reply 18 days ago
Hi,
Dont worry about English, its Ok. I have already tried this. I disabled the 'window.authInProgress'. I was able to draw patterns stored in the SD card. However, there were no thumbnails (and other information), as the frontend tries to get these from the TranquilAPI server (has to be hosted on Cloudflare).
You will need to store the .thr files in the root of sd card and put the relevant information in the manifest.json file, so that the device can detect these files.
I want to test the table with full functionalities, that is why I set up the TranquilAPI server (looks like not the correct way).
Let me know if you figure out setting up the server, it will be of immense help.
Question 26 days ago
Hi Aiden, congratulations on creating such a wonderful project. I am trying to build a similar table for me. I have Printed all the parts and assembled them (with little modification). I have also made the PCB board and mounted all the components. I am able to program the device and also upload the compiled filesystem to the board. The board seems to work fine. SD card is also working as I can see manifest.json file being created on it after the programming. My board creates the access point "Tranquil", I am able to connect to it.
However, on connection, I do not see anything. Page shows an error "ERR_CONTENT_DECODING_FAILED". I am stuck at this point for a few days. I have not made any changes to the Firmware and the Web-Interface. It will be really kind of you if you can help me in solving this issue. Attached are the images of the Setup and the PCB board.
Question 2 months ago
would this work with a HUZZAH32 – ESP32 Feather Board?, when i upload it to the feather it seems to run but when I go to 8.8.4.4 to setup the wifi credentials, the page doesn't exist(too many redirects)
Answer 2 months ago
Did you compile and upload the web interface to the device using PIO sidebar -> Upload Filesystem.
Reply 2 months ago
getting the same result,
ive ordered the same board you used anyway but
I build TranquilVue, put all the files from "dist" output into a folder at the root of TranquilFirmware called "data", upload filesystem, upload.
I've tried the other way upload, upload filesystem, same results
seems to be uploading both fine, is there something im doing wrong?
upload filesystem image log:
pastebin.com/jzf9gPiq
upload log:
pastebin.com/WXdraRgb
serial monitor:
pastebin.com/vRVpLDYm
Reply 26 days ago
Could you get it work in the end?
I am facing the same issue.
Although I have the full pcb board with all the components mounted.
SD card also seems to work fine, I can see the manifest.json file being created on it after the programming.
Reply 2 months ago
I can take a look at it later today, it's most likely due to the SD card not being present. It's supposed to be optional but unfortunately in some places it is hard coded.
Reply 2 months ago
Hey,
Did you get a chance to go through the issue?
The webpage does not open, too may redirects.
Reply 2 months ago
I tried the same on an ESP32 DEVKIT V1 and I was getting the same result, must be something to do with the SD card as you mentioned
6 weeks ago
Great work. On the board design I see 5 of the R0402 10k Ohm resistors but I don't see them on the pictures of the completed board, unless they are so small they aren't obvious. Are they needed?
7 weeks ago
First of all, Aiden, congratulations on your project! I really liked it too, so much that I wanted to replicate it.
During the process, I encountered some difficulties, and with my limited knowledge, I wasn't able to solve them. I hope you can guide me through if it's not too much trouble.
When I try to access through WiFi, the front-end doesn't load. It only loads when I use "npm run dev," and that's the only way I can see something. This would be the first problem I can't figure out how to solve. The next issue is how to make it recognize the patterns for drawing. I can't find the path to place them so they can be executed. I know they are on the SD card. I tried placing them in the root and it doesn't work. The next thing is that it asks for an email and a password, which I also can't deactivate, and I don't know how to log in. As I mentioned, I have limited knowledge. I'm just starting to study programming, and I liked this project to work on during my vacation. That's why I hoped you could guide me a bit. I apologize if my English is not good, it's not my native language. And finally, congratulations again on such an incredible job!
2 months ago
This is really a great work. Thanks for sharing. I am curious about how you restore the sand table every time you finish a painting. It seems that you have not seen the restoration device. I look forward to your reply
2 months ago
why the belt drive? If you are 3d printing stuff, why not print a simple gear? Just wondering. At some point in time I'm going to make something similar.
Reply 2 months ago
Just because it was quieter and had less play
Reply 2 months ago
thanks
2 months ago
Hi! Amazing project! A couple of questions I couldn't find from your instructions:
- you are using a 24V power supply, right?
- the second hall sensor sits on the upper half of the enclosure behind the motor with the belt?
I'm sure I'll have more questions further along. Thanks for the instructions!
Question 2 months ago
if I do a GET for "http://192.168.0.51/" it returns:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/png" href="/icon-192x192.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tranquil</title>
<script type="module" crossorigin src="/assets/e7e8929e.js"></script>
<link rel="stylesheet" href="/assets/38748ddb.css">
</head>
<body>
<div id="app" class="w-full h-full"></div>
</body>
</html>
2 months ago
Very nice. I was expecting to see a slip ring to keep the wires for getting twisted. Is it not listed in the article or was some other solution used?