Projects

Automatic Zen Garden - Part 2

Liam Davies

Issue 68, March 2023

This article includes additional downloadable resources.
Please log in to access.

Log in

Find your inner peace with this automatic Arduino-powered Zen Garden. Who knew watching a steel ball roll making pretty patterns in sand could be so relaxing?

We finished off our Zen Garden project this month by improving our drive mechanism, adding software to the Arduino, and finally, testing it all out.

Note: Video shown here is 125x normal speed (1 frame every 5 seconds).

Last month, we put together our first prototype of our automatic Zen Garden. While we focused a lot on the mechanics of the design, it still had some issues that needed to be fixed before it worked 100% of the time. It was capable of drawing some basic patterns, but the ball bearing we were using to draw often lost attraction to the magnet underneath and was stranded in the sand. Another problem we discovered was that the sand we were using, while fine, wasn’t fine or light enough to move out of the way of the ball. Let’s dig into how we resolved these issues in Part 2 of the Zen Garden!

Finishing Main Build

Parts RequiredJaycarAltronicsCore Electronics
1x Arduino UnoXC9202Z6240ARD-A000066
1x Stepper Driver ShieldLittle Bird Electronics: LB-LR0180J0070ADA324
2x A4988-Compatible Stepper DriversLittle Bird Electronics: PL-2134-ADA736
2x NEMA17 Stepper MotorsYM2756--
1x 6-Core Slip Ring---
4x 8mm Rail Shaft Support--MB85412
2x 8mm Bearing Block--ADA1176
1x 8mm LeadscrewLittle Bird Electronics: AF-1182-MB86050
2x 8mm Aluminium Shaft---
1x Flexible Coupler (5mm to 8mm)TL4036X3222AADA285
2x 8mm Slide Bushings-
1x Snap-Action SwitchLittle Bird Electronics: PL-1404
1x Steel Ball Bearing-
1x 300mm Lazy Susan BearingBunnings: 0080823
3x 2.5mm Hole MagnetsLittle Bird Electronics: SF-COM-08914
WS2812B LED Strip (30 LEDs/m)XC4390
3mm MDF Sheet
Baking Soda
Gorilla Glue
M3 Screws and Nuts
M5 Screws

Parts Required

1x Arduino UnoXC9202
1x Stepper Driver ShieldLittle Bird Electronics: LB-LR0180
2x A4988-Compatible Stepper DriversLittle Bird Electronics: PL-2134
2x NEMA17 Stepper MotorsYM2756
1x 6-Core Slip Ring-
4x 8mm Rail Shaft Support-
2x 8mm Bearing Block-
1x 8mm LeadscrewLittle Bird Electronics: AF-1182
2x 8mm Aluminium Shaft-
1x Flexible Coupler (5mm to 8mm)TL4036
2x 8mm Slide Bushings-
1x Steel Ball Bearing-
3x 2.5mm Hole MagnetsLittle Bird Electronics: SF-COM-08914
3mm MDF SheetBaking Soda

We changed some parts of the build from last month to allow the ball to move easier. We first switched the material to a 3mm MDF board.

We used a jigsaw to cut out a 365mm saw, after tracing a line out with a pen and string. Try and cut this as accurately as possible, but it can be sanded down to size after cutting if necessary.

To keep the sand inside, we designed and 3D printed a circular guard in four sections. They include an internal lip for placing the glass lid over top, as well as a 10mm high slot for inserting the NeoPixel strip.

After carefully aligning the four sections on the MDF, we used some Gorilla Glue and clamps to secure them to the board. Be sure to add Gorilla Glue to the sides of the sections so sand can’t escape between the sections. You may wish to file down the sections if they don’t sit flush against each other. We then left the top board to dry for 24 hours to let everything set properly.

Adding LEDs

To really add another dimension to the lighting of our project, we added some NeoPixel strips to the inside of the 3D print guards. We ALWAYS recommend testing NeoPixels before installing them - 99% of the time, they will work perfectly. However, the other 1% of the time, NeoPixels will cause absolute chaos. Even if one chip is problematic, it can cause data transfer issues for the rest of the strip.

We cut a 30 LEDs/m strip to the correct size for our Zen Garden, and routed the three power and data wires through a hole in the guard sections. We didn’t remove the silicon tubing as we figured it would help keep the sand inside the Zen Garden. The three data and power wires don’t need to be mounted to the main 3D printed system as it doesn’t rotate with the ball.

Upgraded Drivers

We weren’t happy with the motor drivers from Part 1. While we can make it work in the code, the motors are jerky, make a lot of noise, and get very hot thanks to the absence of current-limiting. The LM298N drivers are cheap and easy to use, but are more suited to driving DC motors. They’re also big, bulky and the screw terminals are frustrating to secure small wires into. As a result, we’ve opted to upgrade the drivers to proper stepper drivers.

Before that, though, the first thing we did was disassemble the existing circuit. We removed wires from the screw terminals and made sure to note the power wire colours so we don’t short-circuit anything later.

We also removed the 3D printed stepper mount, which makes the whole project easier to work with. If we ever want to mount some existing hardware in future, such as a WiFi or Bluetooth board, battery bank or more lighting, we now have some extra space to do so!

Now that that’s gone, let’s upgrade our project with some new electronics!

Instead of soldering together a custom shield, we’re now using one purpose-built for Arduino-powered CNC mills, 3D printers and plotters. This shield is most often used with the GRBL firmware which can be fed control commands like a 3D printer. However, we’re going to be writing our own code to interface with the stepper motors and implement our custom Zen garden algorithm.

The shield itself doesn’t have any active electronics besides some beefy decoupling capacitors, but it certainly makes it much easier to use pre-existing stepper drivers. Our new stepper driver of choice is the A4988-compatible series, which is an affordable, powerful and incredibly cross-compatible stepper driver.

They are very simple to drive, have 1/16th microstepping (yes, 16x as many virtual steps!), full current-limiting capabilities and are often used in 3D printers. We ordered a ton of these drivers just because they are so useful. There are many variants of these drivers too. We ordered the DRV8834, which is a low-voltage version for compatibility with our stepper motors.

After soldering on the included male headers, it’s as simple as slotting the drivers into the female headers and

connecting stepper motor coils into the slots. Note that if you want microstepping for additional precision and smoothness, you’ll need to add jumpers to M0, M1 and M2 headers. We set up ours to have 8x microstepping, which lets us move the steppers very smoothly while retaining good torque and speed.

Image Credit Polulu

If you are using microstepping, be sure to use a small screwdriver to change the current limit on the stepper drivers. They rely on this current driver to regulate the microstepping process. If your drivers are ‘jumping’, or only work at certain speeds, you’ll need to appropriately adjust the drivers. The motors don’t require a lot of current in this project, so feel free to set the current lower than you might for other projects.

Be absolutely sure to plug in the drivers in the right orientation. Keep that magic smoke inside the drivers!

We soldered on some new header wires to our stepper motors, ensuring we keep the same wiring order. Then, just plug them into the stepper driver board. This saves us the hassle of dealing with inconsistent and incredibly frustrating screw terminals that don’t hold onto thinner wires properly.

Limit Switch

During our initial code testing, we often found that we’d absent-mindedly leave the gantry system near the limits of the Zen Garden while turning it off. This isn’t an issue, as long as the Arduino knows where it is when it starts running again! After plugging it back in, the Arduino assumes the magnet is in the centre of the garden, and continues to drive the magnet out, smashing it into the bearing blocks and causing a horrible grinding noise.

To avoid this problem, and to make our Zen Garden much more consistent between runs, we’re adding a limit switch so the Arduino can get an absolute reference for its position. Because the limit switch will be mounted in the centre of the motor mounts, the Arduino can just roll the radius motor back until it hits the switch during its setup routine. At this point, it knows it’s at position ‘0’, and future movements will be relative to this position.

The switch we’re using for this is a simple click-style switch that has both open and closed contacts. We only used the ‘C’ (common) and ‘NO’ style contacts, which can be connected to the ‘Y+’ limit switch on the Arduino’s motor shield. We initialise this pin as an input with a pullup resistor so that the voltage becomes 0V when the switch is pressed.

The limit switch is mounted just behind the bearing blocks and can be adjusted depending on how much clearance you want.

Why aren’t we using another limit switch for the ‘turntable’ axis? Because it’s circular, we don’t have any limits to deal with! The motor can spin the turntable indefinitely and never snag any wires or run into any obstacles. The only downside of this is that there is no absolute angle reference - in other words, every time the Arduino turns on, the start angle is where it last stopped. As a result, syncing the NeoPixels to the ball position isn’t possible.

In any case, our physical main build is now finished! Let’s get into writing the software.

Mathematics

We went into a bit of the mathematics last month, but implementing the actual functions in our Zen garden needs some serious work to get it running in code. There is quite a bit of maths knowledge needed, and we initially tried to get the most mathematically correct solution working.

Essentially, our objective was to develop an algorithm that outputs a big list of coordinates for our Arduino to move the motors to. A critical part of this algorithm was generating equidistant points, so it’s not obvious that the beautiful arrangements of circles and polygons is actually made of many points joined together. Because of the complexity of the original equation, generating points mathematically was very challenging.

We enlisted the help of WolframAlpha to rearrange an equation for us:

The key objective of this equation was to figure out how far our turntable has to move before plotting another equally-spaced point. As it turns out, the result of this rearrangement is complex, to say the least.

Let’s be honest, we’re probably not going to be working this out with pen and paper.

We implemented it in Python with a Jupyter Notebook to test if all our hard work (or, rather, WolframAlpha’s hard work) paid off. Sure enough, it works. The points still aren’t quite equally-spaced, but it was good enough.

Awesome-looking patterns appear in the polar plots generated, however, we had serious concerns as to whether the Arduino could handle the size of the equations required. It could take up to a few milliseconds per evaluation, which in the world of microprocessors, is an eternity.

For this reason, we attempted to simplify the algorithm further. We just switched to increasing the angle linearly, and generating sample points as a big list. The Arduino should have an easier time evaluating this.

The Arduino itself has the job of converting our magical theoretical patterns into actual stepper commands. This is an entire field to study in of itself - known as Control Theory - so there will most likely be much better approaches available than the one we are using.

Our strategy is to rotate the turntable at a constant rate and calculate the correct radial positions for the magnet as we turn it. For this to work, the Arduino needs to be aware of where the stepper positions are physically located.

We can do this very easily by working out the physical effect rotating the steppers have. For instance, let’s work out how many steps are required to move the Radial stepper motor from the centre to the outside of the garden:

This calculation simply accounts for every mechanical component required to move the magnet carriage. For example, our lead screw moves 4mm every time it’s turned so we divide the entire result by that amount.

The turntable motor is a similar process:

These calculations can be applied to many different types of projects - it mostly comes down to understanding what parts you’re using, and applying the maths based on their specifications.

The Code

We can talk about as much mathematics as we want, but sooner or later, we need to actually program our Zen Garden! Thanks to our slight redesigning in this part of the project,

we don’t need to delve into any super complicated stepper motor logic, as the drivers will handle that for us.

We chose to use an awesome library to help us move the steppers safely and efficiently, called AccelStepper. It’s similar to the Stepper library built into the Arduino IDE, but includes additional features such as motor acceleration, move speeds, multiple stepper control and fully positional movement.

Each stepper is initialised in the code like this:

AccelStepper Rstepper = AccelStepper(AccelStepper::DRIVER, R_STEPPER_PULSE, R_STEPPER_DIR);
AccelStepper Tstepper = AccelStepper(AccelStepper::DRIVER, T_STEPPER_PULSE, T_STEPPER_DIR);

This tells the AccelStepper library that we have two steppers connected, our Rstepper (the radial axis, moving the magnet in and out), and our Tstepper (the turntable axis, moving the magnet circularly).

Our setup routine consists of initialising our WS2812B LEDs, and setting up our Arduino’s GPIOs. While AccelStepper handles the GPIO logic for the stepper motors, we still need to add some other commands to help things along. A4988-compatible stepper drivers have an “Enable” pin that needs to be turned off to (confusingly) enable the motors. Additionally, we must remember to initialise our limit switch pin to “INPUT_PULLUP” otherwise the magnet carrier will run into itself very quickly!

void setup() {
  FastLED.addLeds<WS2812B, PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(255);
  pinMode(ENABLE_STEPPERS, OUTPUT);
  digitalWrite(ENABLE_STEPPERS, LOW);
  Rstepper.setMaxSpeed(DRAW_R_SPEED);
  Rstepper.setAcceleration(1000);
  Tstepper.setMaxSpeed(DRAW_T_SPEED);
  Tstepper.setAcceleration(1000);
  pinMode(R_STEPPER_LIMIT, INPUT_PULLUP);
  home();
}

Each stepper motor also needs parameters to control its maximum speed and acceleration. While maximum acceleration can be set quite high, be aware that very fast accelerations tend to make a lot of noise and shake the Zen Garden easily. Fast speeds are generally okay, but it may make the magnet less accurate in the sand and will be much louder when running.

Adding the limit switch in this project is really handy, as it allows us to get accurate and repeatable positioning whenever we run the Zen Garden. When it’s first turned on, we need to tell the Radial axis to move back and find where the limit switch is.

void home() {
  Rstepper.setMaxSpeed(HOME_SPEED);
  while(digitalRead(R_STEPPER_LIMIT)) {
    Rstepper.move(-10000);
    Rstepper.run();
  }
  Rstepper.stop();
  Rstepper.setCurrentPosition(0);
  Rstepper.setMaxSpeed(DRAW_R_SPEED);
}

The code above will do that for us. After setting the radial stepper at its homing speed (a lot faster than its regular drawing speed to hurry things along!), we continue to move the stepper backwards until the limit switch reads LOW - i.e. it has been pressed. Once that happens, we instantly stop the stepper motor. This must be instant - if it slows down with any delay, it’ll run into the bearing block and cause all sorts of damage.

After that, we set the position it’s at to 0, which is the same process your 3D printer does at the beginning of each print. We now can reference a position that will be the same every time our project is turned on. For example, calling this code will always move the radial stepper 5000 steps away from the centre:

Rstepper.moveTo(5000);

Drawing the patterns themselves is actually quite a challenge. There are a few factors at play, all of which need to be managed carefully.

Many movements need to be coordinated between the motors. For example, drawing a straight line through the sand will require different speeds of both motors at different times. If it’s not synchronised properly, it won’t be a straight line. Additionally, we must make sure that our motors do not go out of bounds otherwise they will run into the boundaries of the Zen Garden. And finally, the drawing commands must be practical. In other words, we cannot expect that our ball can travel across the garden in 10 seconds if demanded by a pattern.

This could be done a million different ways, but our first approach was to simply drive both motors at constant rates to test if a spiral was practical.

  Rstepper.moveTo(MAX_R_STEPPER_POSITION);
  Tstepper.moveTo(10000000);
  Rstepper.run();
  Tstepper.run();

You can see the results of this code in the first few photos of our ‘Testing’ section. However, for more complex patterns, the easiest way to accomplish the drawing process is to generate destination points once one is reached. That way, we can use our equations discussed before and generate procedural patterns on the fly.

float theta_function(float t) {
  float r = 0.05 * t + 0.05 * sin(10.0 * t);
  Serial.println("t: " + (String)t + " => r: " + (String)r);
  return r;
}

We then wrote the above function that allows us to directly transfer equations from Desmos (or any other graphing calculator) into code. There are also some supporting functions that help convert the function above into a target position for the stepper motors. We also need to make sure the stepper motors don’t go outside of the bounds of the Zen Garden, so we added this code to handle this:

uint32_t getNewRPos() {
  uint32_t t_pos = Tstepper.currentPosition();
  //Evaluate position in rads based off motor pos.
  float radians_pos = (float)t_pos * PI / (STEPS_PER_T_ROTATION / 2.0);
  //This function is scaled from 0 to 1, 
  //we need to check it's within bounds.
  float radius_01 = theta_function(radians_pos);
  radius_01 = constrain(radius_01, 0.0, 1.0);
  uint32_t r_pos = radius_01 * MAX_R_STEPPER_POSITION;
  return r_pos;
}

This is getting pretty mathematical, but the key objective of this code is to find a new position for our radial axis to target - i.e. where our magnet should go next. Given that we’re operating with circles, we’re using Pi and a few other mathematical tricks to get the steps into the right positions.

To ensure everything was working as expected, we uploaded some code to the Arduino to verify the input and outputs of our functions. The input - the current angle of the turntable - and the output - the radial position of the magnet was printed to console so we can verify it works:

You’ll notice there are some negative values - this is okay, our magnet won’t go backwards or run into anything. Our code above will constrain this to 0 anyway. The T value represents the table angle in radians - so a T value of 6.28 (= 2 * PI) means the table has rotated once.

Testing

To test our Zen Garden, we placed it on a workbench and hooked up a lab power supply to it. One channel needs to be set to our stepper motor power supply, and the other needs to be set to 5V to power the Arduino.

However, before we do any real testing, let's pour in our ‘sand’! We tried using bicarbonate soda for this, as the sand we used in Part 1 just wasn’t fine enough. We saw some Zen Garden projects online using bicarbonate soda because it’s cheap and finer than most sands. Just don’t get it wet!

We also had to use a spare paint roller to get rid of the lumps before we did any testing. Make sure to not push it too much up against the NeoPixels as the ball can’t reach far enough out to drag the ‘sand’ back in.

For a first test run, we need to make sure the bicarbonate soda is smooth enough that it doesn’t interfere with the ball’s movement. We used a cardboard sheet to smooth the surface.

Finally, we programmed a simple spiral into the code and let it run for 10-20 minutes. As you can see, the first spiral was not very compact - it was mainly to prove that the code was working.

For the second test, we changed the speeds of the motors and adjusted the pattern. This test was much more interesting, and this time the ball was overlapping its spiral path every time it went around, creating a beautifully fine pattern in the sand.

While the ball was skipping slightly in the tray, it is still easily capable of drawing intricate patterns into the sand. We let the Zen Garden run for a while, and tested some other maths functions in the code.

We found that while it was possible to run the motors really fast, it reduces the accuracy of the motors and can lead to some drifting accuracy problems. And of course, it’s not really very “Zen” to have motors whirring at Mach 4 in your living room. It’s much more relaxing to see the ball slowly rolling around the board and watch it gradually overlap the old patterns.

Not to blow our own horn or anything, but we were thoroughly impressed by the performance of our Zen Garden. It’s easily capable of drawing awesome patterns and running virtually indefinitely in your living room. While it won’t be as accurate as a fancy Sispyhus sand art table, for the minimal cost required for the parts, it’s definitely worth it. If you’re going to be running it 24/7 for a long time, definitely consider tidying up wires and keeping the motors cool. If a wire snag occurs, it’s going to make a mess or break some parts! Additionally, we suggest adding an enclosure around the entire build to clean up the appearance and prevent dust and curious fingers from getting in.

Where To From Here?

This is a project that we spent quite a bit of time on, especially considering that we designed our own rotatable motor system and derived the mathematics for it. We took a bit of an alternative route compared to what many makers do for projects like these - many use pre-made 3D printer frames, which are stable and easy to get working, but take the fun out of designing your own control mechanism.

Our polar design is a fully circular system, so it’s compact and doesn’t suffer from the “square peg in a round hole” problem that a rectangular 3D printer frame does on a circular table - our system uses most of the area available.

Obviously, this project is designed to be an aesthetic addition to a maker’s office or living room - perhaps as a coffee or book table. We encourage you to get creative and find new ways of using magnets to draw new things. For instance, we’ve seen variants of these Zen Garden projects that use magnetic iron filings to create beautiful abstract shapes and animations.

IMAGE CREDIT: Vsauce, YouTube

If you’re not interested in the Zen Garden concept, but want to use the radial motor turntable idea, we’ve provided the 3D models to be used with a Lazy Susan turntable and any ideas you have. A solar system scale-model or a clever persistence-of-vision project comes to mind.

Let us know what you come up with on social media!

Link to Zen Garden Part 1