Project tutorial
Sand Art Display

Sand Art Display © GPL3+

A Japanese zen garden built into a glass-covered picture frame.

  • 2,579 views
  • 2 comments
  • 13 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
see the construction manual PDF for all components.
×1
5 V stepper motor 28BYJ-48 with driver board ULN2003.
×2

Necessary tools and machines

09507 01
Soldering iron (generic)
see the construction manual PDF for all tools needed.

Apps and online services

About this project

Imagine a Japanese zen garden built into a glass-covered picture frame. Below the frame are electronics and robotics, creating geometric patterns and images in the sand. The effect is enchanting and magical, it is really zen-like to see ever-changing patterns and images slowly appear in the sand.

The Sand Art Display is a modern version of this traditional zen sand garden. It is a drawing machine that creates mandala-like patterns in the sand. The operation of this display is as follows: under the glass top is a thin layer of sand, under thes and a twin-engine robot moves a strong magnet to steer a steel ball through the sand. The robot is controlled by an Arduino mini computer playing a pattern program, similar to the way a music player plays MP3 songs. The table calibrates itself when plugged in and after this calibration a number of pre-programmed patterns can be selected using the push buttons.

Code

Display3Arduino
Arduino program for 9 patterns:
Waffle, Plough,Pong, Sine, Hello world, Mad Ant, Spiral, Christmas Tree and Ferrari.
The selection leds will light up every 5 seconds. When you press a button when the leds are ON you can select the first set of three patterns, when you press a button when the selection leds are flashing ON/OFF you can select the second set and when the leds are OFF you can select the third set.
/*
Display3: this is a combined program for 3x3 patterns.
For the first set of three patterns press a button when alle select leds are ON.
For the second set of three patterns press a button when alle select leds are flashing ON/OFF.
For the third set of three patterns press a button when alle select leds are OFF.
These leds will be ON or flashing or OFF every 5 seconds.
Stepper Motor Control progam for an X-Y table named "Sand Art Display".
This program drives two unipolar 5V DC stepper motors type 28BYJ-48.
The motors are attached to digital pins 4,5,6,7 and 8,9,10 and 11 of the Arduino UNO, 
via a driver board with IC ULN2003.
The motor power supply on this board is a separate 5V DC supply. 
GND is connected to the GND of the Arduino. 
The motors drive both axis of the sand art table with a 250 mm rack gear.
It starts the movement blue, yellow or white when the motors are resting in the full left & up position.
See www.apinventions.wordpress.com for the Sand Art Display building instructions.
Created July 12 2020 by Alex Pikkert.
*/

#include <Stepper.h>
//this sequence is special for the 28BYJ-48 stepper motor.
Stepper myStepperX(1024, 8,10,9,11);Stepper myStepperY(1024, 4,6,5,7);

int button    = A1; // analog button value 736(white)/451(yellow)/14(blue)
int select    = 0;  // select which road to follow... 
int resetled  = A2; // green led ready for instructions
int whiteled  = A3; // path 1
int yellowled = A4; // path 2
int blueled   = A5; // path 3
int xstop     = 12; // port end position left or right
int ystop     = 3;  // port end position up or down
int speed1    = 10; // slow speed
int speed2    = 15; // medium speed 
int speed3    = 25; // fast speed
int speed4    = 30; // super fast speed
int up        = 50; // length of standard step up
int down      = -50;// length of standard step down  
int left      = 50; // length of standard step left
int right     = -50;// length of standard step right
int n=0;            // counter flag up/down
int m=0;            // counter flag left/right
int x=0;            // left/right number of steps
int y=0;            // up/down number of steps
int d=0;            // counter for step difference
long randNumber;    // counter for random steps
double cosinus;     // cosine value for X
double sinus;       // sine value for Y
int i;              // steps counter
int j;              // steps counter
int ystep;          // Y step increment
int yprev;          // Y step store in memory 
int ymax;           // Y max amplitude 
int xmax;           // X max amplitude 
int xstep;          // X step increment
int xprev;          // X step store in memory 
int radius;         // circle diameter
int a;              // select which set is active (select1 or select2 or select3)
int start;          // start angle of a circle
int finish;         // end angle of a circle

void setup() {
myStepperX.setSpeed(speed3);myStepperY.setSpeed(speed3);
pinMode(3,INPUT_PULLUP);pinMode(12,INPUT_PULLUP);pinMode(A1,INPUT_PULLUP);
pinMode(A2,OUTPUT);pinMode(A3,OUTPUT);pinMode(A4,OUTPUT);pinMode(A5,OUTPUT);
myStepperX.step(5*left);myStepperY.step(5*up);// always move to left/up corner start position
}

void loop() {
randomSeed(analogRead(A0));
myStepperX.setSpeed(speed3);myStepperY.setSpeed(speed3);
digitalWrite(whiteled,LOW);digitalWrite(yellowled,LOW);digitalWrite(blueled,LOW);
y = digitalRead(ystop);if(y==HIGH) {myStepperY.step(4*up);}
x= digitalRead(xstop);if(x==HIGH) {myStepperX.step(4*left);}

if (y==LOW && x==LOW) // left/up start position
{ digitalWrite(resetled,HIGH);

// select which set of three patterns is active (select1 or select2 or select3)  
a=a+1;
if (a <= 10000){select1();}
if (a> 10000 && a <= 10040){select2();}
if (a>= 10040 && a <= 20000){select3();}
if (a > 20000) {a=0;}
}
}

void select1()
{  
  digitalWrite(whiteled,HIGH);digitalWrite(yellowled,HIGH);digitalWrite(blueled,HIGH);
  select = analogRead(button);
  if (select <150){digitalWrite(whiteled,LOW);digitalWrite(yellowled,LOW);blue1();}
  if (select >150 && select <600){digitalWrite(whiteled,LOW);digitalWrite(blueled,LOW);yellow1();}
  if (select >600 && select <800){digitalWrite(yellowled,LOW);digitalWrite(blueled,LOW);white1();}
}

void select2()
{  
  delay(50);digitalWrite(whiteled,HIGH);digitalWrite(yellowled,HIGH);digitalWrite(blueled,HIGH);
  delay(50);digitalWrite(whiteled,LOW);digitalWrite(yellowled,LOW);digitalWrite(blueled,LOW);
  select = analogRead(button);
  if (select <150){digitalWrite(whiteled,LOW);digitalWrite(yellowled,LOW);digitalWrite(blueled,HIGH);blue2();}
  if (select >150 && select <600){digitalWrite(whiteled,LOW);digitalWrite(blueled,LOW);digitalWrite(yellowled,HIGH);yellow2();}
  if (select >600 && select <800){digitalWrite(yellowled,LOW);digitalWrite(blueled,LOW);digitalWrite(whiteled,HIGH);white2();}
}

void select3()
{  
  digitalWrite(whiteled,LOW);digitalWrite(yellowled,LOW);digitalWrite(blueled,LOW);
  select = analogRead(button);
  if (select <150){digitalWrite(blueled,HIGH);blue3();}
  if (select >150 && select <600){digitalWrite(yellowled,HIGH);yellow3();}
  if (select >600 && select <800){digitalWrite(whiteled,HIGH);white3();}
}

//WAFFLE 
void white1(){
myStepperX.setSpeed(speed2);myStepperY.setSpeed(speed2);
digitalWrite(whiteled,HIGH);digitalWrite(resetled,LOW);
if(d==0){
if(n==0){myStepperY.step(5*down);}
y = digitalRead(ystop);if(y==LOW && n==0){myStepperX.step(11*right);n=1;x=x+1;}
if(n==1){myStepperY.step(5*up);}
y = digitalRead(ystop);if(y==LOW && n==1){myStepperX.step(11*right);n=0;x=x+1;}
if(x >7){d=1;y=0;n=0;}
}
if (d==1){
if(n==0){myStepperX.step(5*left);}
x = digitalRead(xstop);if(x==LOW && n==0){myStepperY.step(11*down);n=1;y=y+1;}
if(n==1){myStepperX.step(5*right);}
x = digitalRead(xstop);if(x==LOW && n==1){myStepperY.step(11*down);n=0;y=y+1;}
while(y >5){};
}
white1();
}

//PLOUGH
void yellow1(){
myStepperX.setSpeed(speed2);myStepperY.setSpeed(speed2);
digitalWrite(yellowled,HIGH);digitalWrite(resetled,LOW);
x=73-d;myStepperX.step(x*right);
y=56-d;myStepperY.step(y*down);
d=d+3;
x=73-d;myStepperX.step(x*left);
y=56-d;myStepperY.step(y*up);
d=d+3;
while(y<5 || x< 5){} ;
yellow1();
}

//PONG
void blue1(){
myStepperX.setSpeed(speed2);myStepperY.setSpeed(speed2);
digitalWrite(resetled,LOW);

y = digitalRead(ystop);if(y==LOW && n==0){myStepperY.step(4*down);n=1;}
y = digitalRead(ystop);if(y==HIGH && n==1){myStepperY.step(2*down);}
y = digitalRead(ystop);if(y==LOW && n==1){myStepperY.step(4*up);n=0;} 
y = digitalRead(ystop);if(y==HIGH && n==0){myStepperY.step(2*up);}

x = digitalRead(xstop);if(x==LOW && m==0){myStepperX.step(4*right);m=1;}
x = digitalRead(xstop);if(x==HIGH && m==1){myStepperX.step(2*right);}
x = digitalRead(xstop);if(x==LOW && m==1){myStepperX.step(4*left);m=0;} 
x = digitalRead(xstop);if(x==HIGH && m==0){myStepperX.step(2*left);}
blue1();
}

//SINE SHAPE
void white2(){
myStepperX.setSpeed(speed4);myStepperY.setSpeed(speed4);
digitalWrite(resetled,LOW);
if (d==0)
{myStepperY.step(60*down);myStepperY.step(30*up);
myStepperX.step(76*right);myStepperX.step(76*left);
d=1;delay(500);}
ymax=1400;drawsinus();
ymax=900;drawsinus();
ymax=400;drawsinus();
ymax=-1400;drawsinus();
ymax=-900;drawsinus();
ymax=-400;drawsinus();
while(i>360){};
}

void drawsinus(){
for (i = 1; i <= 360; i=i+2)
 {sinus = sin(i*0.0175);
 y=(sinus*ymax);
 ystep=(y-yprev);
 myStepperY.step(ystep);
 myStepperX.step(0.44*right);
 yprev=y;}
 
 myStepperX.step(76*left);
}

//MESSAGE HELLO WORLD
void yellow2(){
x=9;y=12;
myStepperX.setSpeed(speed4);myStepperY.setSpeed(speed4);
digitalWrite(resetled,LOW);
//H
myStepperY.step(2*y*down);
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperY.step(y*up);
myStepperY.step(2*y*down);

//E
myStepperX.step(x*right);
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperX.step(x*left);
myStepperY.step(y*down/2);
myStepperX.step(x*right);
myStepperX.step(x*left);
myStepperY.step(y*down/2);
myStepperX.step(2*x*right);

//LL
myStepperY.step(2*y*up);
myStepperY.step(2*y*down);
myStepperX.step(x*right);
myStepperY.step(2*y*up);
myStepperY.step(2*y*down);

//O
myStepperX.step(x*right);
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperY.step(y*down);
myStepperX.step(1.3*x*left);
myStepperX.step(2.3*x*right);
myStepperY.step(2.8*y*down);
myStepperX.step(8*x*left);

//W
myStepperY.step(y*up);
myStepperY.step(y*down);
myStepperX.step(0.6*x*right);
myStepperY.step(0.5*y*up);
myStepperY.step(0.5*y*down);
myStepperX.step(0.6*x*right);
myStepperY.step(y*up);
myStepperY.step(y*down);

//O
myStepperX.step(x*right);
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperY.step(y*down);
myStepperX.step(1.3*x*left);
myStepperX.step(2.3*x*right);

//R
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperX.step(x*left);
myStepperY.step(y*down);

//L
myStepperX.step(1.8*x*right);
myStepperY.step(2*y*up);
myStepperY.step(2*y*down);

//D
myStepperX.step(x*right);
myStepperY.step(y*up);
myStepperX.step(x*right);
myStepperY.step(y*down);
myStepperX.step(0.3*x*right);
myStepperX.step(0.3*x*left);

while(x==9){};
}

//MAD ANT
void blue2(){
myStepperX.setSpeed(speed4);myStepperY.setSpeed(speed4);
digitalWrite(resetled,LOW);
if (d==0)
{myStepperX.step(10*right);myStepperY.step(10*down);}
d=1;
randNumber= random(-10,10);myStepperX.step(randNumber*left);
x= digitalRead(xstop);if(x==LOW) 
{myStepperX.step(5*left);
x= digitalRead(xstop);if(x==HIGH) {myStepperX.step(15*left);}
else{myStepperX.step(15*right);}
}
randNumber= random(-10,10);myStepperY.step(randNumber*down);
y= digitalRead(ystop);if(y==LOW) 
{myStepperY.step(5*down);
y= digitalRead(ystop);if(y==HIGH) {myStepperY.step(15*down);}
else{myStepperY.step(15*up);}
}
blue2();
}

//SPIRAL SHAPE 
void white3(){
myStepperX.setSpeed(speed3);myStepperY.setSpeed(speed3);
digitalWrite(resetled,LOW);
if (d==0)
{myStepperX.step(36*right);
radius=1500;ymax=radius; xmax=radius;yprev=radius;
d=1;delay(500);}

for (j = 1; j<9; j=j+1)
{drawspiral();}
while(d==1){};
}

void drawspiral(){

for (i = 90; i <450; i=i+2)
{
ymax=radius;xmax=radius;
sinus = sin(i*0.0175);
cosinus = cos(i*0.0175);
y=(sinus*ymax);
x=(cosinus*xmax);
ystep=(y-yprev); yprev=y;
xstep=(xprev-x); xprev=x;
myStepperY.step(ystep);
myStepperX.step(-xstep);
radius =radius-1;
}
}

//CHRISTMAS TREE
void yellow3(){
myStepperX.setSpeed(speed4);myStepperY.setSpeed(speed4);
digitalWrite(resetled,LOW);
y=56;myStepperY.step(y*down);
x=74;myStepperX.step(x*right);
x=36;myStepperX.step(x*left);
y=10;myStepperY.step(y*up);
for (i = 1; i <7; i=i+1)
{
j=5;
x=37-i*j;myStepperX.step(x*left);
x=2*(37-i*j);myStepperX.step(x*right);
x=37-i*j;myStepperX.step(x*left);
y=6;myStepperY.step(y*up);
}
y=5;myStepperY.step(y*up);
while(y>0){} ;
}

//FERRARI
void blue3(){
myStepperX.setSpeed(speed4);myStepperY.setSpeed(speed4);
digitalWrite(resetled,LOW);
y=45;myStepperY.step(y*down);
x=76;myStepperX.step(x*right);
x=52;myStepperX.step(x*left);
radius=300; start=270; finish=720; drawcircle();
x=10;myStepperX.step(x*left);
y=15;myStepperY.step(y*up);
x=60;myStepperX.step(x*right);
x=50;myStepperX.step(x*left);
for (j = 1; j <6; j=j+1)
{
x=j;myStepperX.step(x*right);
y=j;myStepperY.step(y*up);
}
x=10;myStepperX.step(x*right);
y=15;myStepperY.step(y*down);
y=15;myStepperY.step(y*up);
x=20;myStepperX.step(x*right);
y=15;myStepperY.step(y*down);
x=10;myStepperX.step(x*right);
y=15;myStepperY.step(y*down);
x=25;myStepperX.step(x*left);
radius=300; start=180; finish=720; drawcircle();
x=10;myStepperX.step(x*left);
while(radius>0){} ;
}

void drawcircle(){
 for (i = start; i <finish; i=i+2)
{
ymax=radius;xmax=radius;
sinus = sin(i*0.0175);
cosinus = cos(i*0.0175);
y=(sinus*ymax);
x=(cosinus*xmax);
ystep=(y-yprev); yprev=y;
xstep=(xprev-x); xprev=x;
myStepperY.step(ystep);
myStepperX.step(-xstep);
}
}

Custom parts and enclosures

construction manual
construction manual

Schematics

Sand Art Display wiring diagram
Sand art display diagram sgtoxcopie

Comments

Add projectSign up / Login