Sand Turner

The Idea

I have this desk toy.

pasted-image-20250210210547.pngSand flows between bubbles, making patterns. When the sand runs out, you flip it over and the process repeats. This takes between 1 and 5 minutes. The problem, at least for me, is that it is fascinating for about a day before the friction of flipping over is just enough to leave it on a shelf and forget about it.

Wouldn't it be cool if it flipped itself? What if it was more kinetic sculpture and less toy. It could be mounted on a wall, run all the time, and be different every time you look at it.

First Swing

The quickest thing I could come up with was essentially 2 plates with m3 bolts through them. One bolt had a bearing on it and the other had a plastic roller and stepper motor. An old arduino controls the stepper and a usbc breakout supplies 5v to both. This worked surprising well, so well that I fell for it. Thinking the job was done, I slapped it into a case that held the plates.
Pasted image 20250210212632.png
I added some LEDs that brighten when the disk is moving and dim when it is not. I put a lid on it, plugged it in, and called it good.
Pasted image 20250210213256.png
It ran continuously for about a week before the plastic pins welded themselves into plastic holes and the motor siezed.

Second attempt

Pasted image 20250211140130.png
On my next go, I caved to common sense. 4 bearings, with one on each side of each axel. This means there are not plastic on plastic wear surfaces and the weight of the disk is not transferred into the motor.sand_turner.jpg

wiring.jpg

So far, so good. This version has been running continuously on my desk for a couple weeks now.

Arduino Code

#include <AccelStepper.h>

// Define step type and steps per revolution
#define FULLSTEP 4
#define STEP_PER_REVOLUTION 2048

// Define step counts for 90 to 270 degrees
#define MIN_STEPS 3800   // 90 degrees
#define MAX_STEPS 11400  // 270 degrees
#define STEPS_TO_DEGREES (180.0 / 7600.0) // Convert steps to degrees

AccelStepper stepper_1(FULLSTEP, 11, 9, 10, 8);

// Define the LED pin (PWM-enabled pin)
#define LED_PIN 6

// Direction toggle variable
bool directionForward = true;

// Define brightness levels
#define BRIGHTNESS_MIN 64  // 25% brightness
#define BRIGHTNESS_MAX 255 // 100% brightness
#define BRIGHTNESS_STEP 5  // Smoothness step size

void setup() {
  Serial.begin(9600);

  // Configure stepper_1 with faster speed and acceleration
  stepper_1.setMaxSpeed(600.0);   // Max speed (steps per second)
  stepper_1.setAcceleration(500.0); // Acceleration (steps per second squared)
  stepper_1.setSpeed(100);         // Initial speed
  stepper_1.setCurrentPosition(0); // Set starting position to 0

  // Initialize the LED pin
  pinMode(LED_PIN, OUTPUT);
  analogWrite(LED_PIN, BRIGHTNESS_MIN); // Set LED to 25% brightness initially
}

void smoothTransition(int fromBrightness, int toBrightness) {
  if (fromBrightness < toBrightness) {
    // Gradually increase brightness
    for (int brightness = fromBrightness; brightness <= toBrightness; brightness += BRIGHTNESS_STEP) {
      analogWrite(LED_PIN, brightness);
      delay(10); // Adjust delay for smoother or faster transitions
    }
  } else {
    // Gradually decrease brightness
    for (int brightness = fromBrightness; brightness >= toBrightness; brightness -= BRIGHTNESS_STEP) {
      analogWrite(LED_PIN, brightness);
      delay(10); // Adjust delay for smoother or faster transitions
    }
  }
}

void loop() {
  // Randomize the number of steps for the rotation (90 to 270 degrees)
  long randomSteps = random(MIN_STEPS, MAX_STEPS + 1); // +1 to include MAX_STEPS in range

  // Adjust direction based on toggle
  if (!directionForward) {
    randomSteps = -randomSteps; // Make the steps negative for reverse direction
  }

  // Calculate the degrees rotated
  float rotationDegrees = abs(randomSteps) * STEPS_TO_DEGREES;

  // Calculate the proportional wait time in milliseconds
  long waitTime = rotationDegrees * 666.67; // Degrees-to-ms conversion (1° -> ~666.67 ms)

  // Smoothly increase LED brightness to 100% during rotation
  smoothTransition(BRIGHTNESS_MIN, BRIGHTNESS_MAX);

  // Rotate stepper_1
  stepper_1.enableOutputs();
  stepper_1.move(randomSteps); // Use move() for relative movement
  while (stepper_1.distanceToGo() != 0) {
    stepper_1.run();
  }

  // Smoothly decrease LED brightness back to 25% after rotation
  smoothTransition(BRIGHTNESS_MAX, BRIGHTNESS_MIN);

  Serial.print(F("Stepper_1 rotated by "));
  Serial.print(abs(randomSteps)); // Display positive value for steps
  Serial.print(F(" steps (~"));
  Serial.print(rotationDegrees); // Display rotation in degrees
  Serial.print(F(" degrees). Waiting for "));
  Serial.print(waitTime / 1000); // Display wait time in seconds
  Serial.println(F(" seconds."));

  // Toggle direction for the next rotation
  directionForward = !directionForward;

  // Wait proportional to the rotation
  stepper_1.disableOutputs();
  delay(waitTime); // Proportional wait time
}

Supplies

Printed Parts

  • 1x Shell Front.stl
  • 1x Shell Back.stl
  • 2x pin.stl
  • 2x roller.stl

Assembly

  1. Print the above parts.
  2. Place the bearing in the slots in the front shell. They should be a friction fit and this is easiest to do with the shell horizontal.
  3. If using the silicone rings, place them on the rollers
  4. Place the rollers between the bearings and pass the pins through.
  5. On the driven roller (the side with motor mounting holes), peal back the silicone ring (if used) and place either CA Glue or a self tapping m2 screw in the locking hole to secure it to the pin.
  6. Wire and flash the arduino as shown here: diagram here (found): https://newbiely.com/images/tutorial/arduino-nano-uln2003-28byj-48-stepper-wiring-diagram.jpg
  7. Attach the stepper motor with the m3 bolts. They are simply strewed into the plastic. m3 nuts can be used as spacers but are not strictly necessary .
  8. (optionally) solder your LEDs to ground and D6 on the arduino.
  9. Attach the usbc 5v breakout to the back shell with double sided tape or hot glue
  10. Close the shell halves. I left plenty of room for even very messy wiring.