Introduction: World’s First Cycloid Art Table: How I Built This Arduino-Powered Spirograph Machine.

Welcome to my latest project: the world’s first cycloid art table! In this video, I’ll take you on a journey through the design, build process, and intricate workings of this unique sinograph machine powered by Arduino.


Link to the code and 3d files https://drive.google.com/drive/folders/14vF2SCqIhi2N2RiJ0s_KFvqKJnszvHVh?usp=sharing

Supplies

17HS4023 Stepper motors x2 https://amzn.to/4cXS9fl

Arduino CNC shield x1 https://amzn.to/4dawcKg

Arduino UNO x1 https://amzn.to/4dawcKg

TMC2209 Stepper drivers x2 https://amzn.to/4dix3bB

BD139 N Channel Mosfet x1 https://amzn.to/3WBY7gq

N52 15x3mm Magnets x1 https://amzn.to/4cZmWbX

8mm Steel Ball bearings x1 https://amzn.to/3SoOjUC

G624ZZ Flanged Ball Bearing 3x13x5mm x1 https://amzn.to/4dmPYSR

10K Pots x2 https://amzn.to/3Wp1ciR

1Meter 12volts 3mm cob LED strip x1 https://amzn.to/3Wpx070

600x600x3mm wood sheets x4 https://amzn.to/3ydhMKq

16cm circular glass table top x1 https://amzn.to/3WFRSsf

12V battery pack x1 https://amzn.to/3Yn9vyg

Table sand x1 https://amzn.to/4fjyWXs

Jumper wires, solder etc. https://amzn.to/3Yn9vyg

Step 1: Assemble the Base and Gears

Base Assembly Instructions:

Laser Cut the Base:

Use a laser cutter to cut the 3mm wood according to the provided design specifications.

Assemble the Base:

Refer to the provided images for guidance on how to assemble the base pieces.

Ensure all pieces fit together snugly and securely.

Secure the Base:

Once all the pieces are properly aligned and assembled, secure them with wood glue or screws as needed.

Allow any glue to dry completely before proceeding to the next steps.

Attach the gears and the arm together using 5mm dowels.

Ensure the dowels fit snugly into the holes for a secure connection.

Install Bearing:

Place a bearing at the bottom of the big gear. This will allow the gear to rotate smoothly.

Step 2: Assemble the Table Top

Table Top Assembly Instructions:

Prepare the Table Top:

Cut a large circle with a circumference of 1 meter.

Glue the wall pieces together to prevent sand from falling off the table. These pieces should interlock like puzzle pieces.

Secure the Walls:

Clamp all the wall pieces together to ensure a tight fit.

Allow the glue to dry completely before proceeding.

Attach the LED Strip:

Place the LED strip on the topmost layer of the table walls.

Ensure it is evenly distributed around the circumference.

Install the Top Ring:

Position the top ring, which should be 5mm wider than the rest of the table structure. This will hide the LED strip beneath it.

Attach the Copper Strip:

Place a copper strip along the circumference of the circle to give an aesthetic look.

Assemble the Table:

Place the completed table top onto the vertical dowels of the table base.

Ensure it is securely fitted and stable.

Step 3: The Electronics

CNC Arduino Shield and Component Assembly Instructions:

Install the CNC Arduino Shield:

Plug the CNC Arduino shield directly onto the Arduino board. This is a plug-and-play connection.

Soldering the Power Switch:

Solder the ground wire from the CNC board to one side of the power switch.

Solder a wire from the other side of the power switch to your power source (either a battery or a 12V power adapter).

Soldering the Potentiometers:

Solder three wires to each potentiometer:

  • The outer legs are for VCC and GND.
  • The middle leg is the signal wire.

Connect the left potentiometer to the Abort pin on the CNC shield.

Connect the right potentiometer to the Hold pin on the CNC shield.

Connecting the LED Strip:

Solder wires to each of the three legs of the N-channel MOSFET:

  • Connect the first leg (gate) to the Z- pin on the CNC shield.
  • Connect the drain of the MOSFET to the ground wire of the LED strip.
  • Connect the source of the MOSFET to a ground pin on the CNC board.


Step 4: Upload the Code and Enjoy!

// BY NEWSON'S ELECTRONICS
// The Spirograph Machine can draw any function in the sand
// it's great way to learn the mathematics of art.
// JULY 10,2024

int state = 1;                                         // used to determine if inOut motor changes direction
int count = 0;                                         // Count the number of pedels.
int pause_time = 3000;                                 // Time for delay between patturn sequence
byte knobs = 0;
byte drawing=0;
byte clearing=0;
byte clearInOut=0;

int xt=0;

//IR remote code does not need a library NEC protocal.
#define IR_Input 9 // connected to x-limit switch.
#define Sync_Time 3000
#define One_Time 1000
#define Zero_Time 400
volatile byte Bit_Count = 0;
volatile byte Byte_Count = 0;
volatile byte Byte_Ready = false;
volatile byte IR_Bytes[4];
volatile unsigned long Start_Time = 0;
volatile unsigned long Pulse_Width;
//IR variables

// circle size radius = 100 units
byte pause = 0;
#define enPin_rot 12     // Stepper motor enable pin
#define lights_pin 11    //z switch
#define stepPin_rot 2    // X axis step pin
#define dirPin_rot 5     // X axis direction pin
#define enPin_InOut 8    // Stepper motor enable pin
#define stepPin_InOut 3  // Y axis step pin
#define dirPin_InOut 6   // Y axis direction pin
#define PI 3.1415926535897932384626433832795

// Changable variables.
#define rot_total_steps 16000.0
#define inOut_total_steps 4300.0  //4300.0
#define desired_scale 100.0


int knob1 = 0;         // POT 1 : LED BRIGHTNESS
int knob2 = 0;         // POT 2 : DRAW SPEED
int brightness = 0;  // Starting brightness for LEDs MAX IS 255
float speed_delay = 1;


//VARIABLES FOR REVERSE KINEMATICS
volatile float r = 0;  // length of extended arm
volatile float x = 0;
volatile float y = 0;  // current location of ball
volatile float x2 = 0;
volatile float y2 = 0;
float xLoc = 0;
float yLoc = 0;
float radAngle = 0;
float rotAngle = 0;


int offset = 0;  // USED TO COUNTER THE GEARS RATIO 10:1
int moving = 0;

int inOutSteps = 0;  /// Y
int inOutStepsTo = 0;
int rotSteps = 0;  //  X
int rotStepsTo = 0;
byte rotON = 0;
byte inOutON = 0;

int ratio_select = 7;


// Variables to store results
float rTo, angleTo;

int pwm = 0;   // ROT MOTOR PWM
int pwm2 = 0;  // INOUT MOTOR PWM

byte inOut = 1;  // 1=OUT, 0=IN
byte ccwCW = 1;  // 0=CCW 1=CW

int speed_InOut = 100;  // speed of second motor
int speed_rot = 100;    // speed of first motor

// change in distanceq

int dx, dy;  //delta x and y
int go = 0;
float m = 0;  //slope between points
float m2 = 0;
float mx, my;  //move distance


int star[] = {-11,5,-21,-9,-4,-9,5,-23,10,-6,25,-1,12,9,12,26,-1,16,-16,21,-11,5,-23,7,-42,-19,-11,-18,6,-44,15,-14,44,-4,20,14,20,46,-4,27,-33,37,-23,7,-33,8,-58,-27,-17,-26,7,-61,19,-20,59,-6,25,19,26,62,-7,36,-46,49,-33,8,-41,9,-71,-33,-21,-32,8,-74,23,-24,70,-8,30,22,31,74,-9,43,-56,59,-41,9,-48,11,-83,-39,-25,-37,8,-87,26,-29,82,-10,34,25,36,86,-11,49,-66,69,-48,11,};

int spiral[]={3,0,-7,4,-14,-5,-6,-16,8,-11,7,6,-10,10,-19,-7,-5,-22,15,-11,10,11,-14,13,-23,-11,-2,-26,20,-10,10,17,-19,15,-25,-14,2,-29,25,-7,9,22,-24,15,-27,-19,7,-32,29,-2,7,29,-30,16,-28,-25,13,-35,35,3,4,37,-38,15,-30,-33,21,-38,42,11,-1,46,-48,14,-30,-43,32,-41,49,22,-9,57,-60,10,-29,-54,45,-42,54,35,-19,68,-73,4,-25,-67,59,-41,58,51,-33,77,-84,-5,-19,-79,74,-36,58,68,-48,83,-94,-17,-10,-90,87,-28,54,83,};


//int star[] = {73, -68, 73, -68} ;
int maze[] = {73, -68, 73, -68, 73, -60, 60, -60, 60, -80, 59, -81, 55, -84, 50, -87, 46, -89, 46, -89, 46, -87, 33, -87, 33, -95, 30, -96, 26, -97, 21, -98, 16, -99, 11, -100, 6, -100, 6, -100, 6, -87, 20, -87, 20, -74, 6, -74, 6, -60, 33, -60, 33, -74, 46, -74, 46, -34, 33, -34, 33, -47, 6, -47, 6, -34, 20, -34, 20, -20, 6, -20, 6, -7, 33, -7, 33, -20, 46, -20, 46, -7, 73, -7, 73, -20, 60, -20, 60, -47, 73, -47, 73, -34, 86, -34, 86, -47, 88, -47, 90, -44, 92, -39, 93, -35, 95, -30, 96, -25, 97, -20, 97, -20, 86, -20, 86, -7, 99, -7, 99, -5, 99, 0, 99, 5, 99, 10, 98, 15, 97, 20, 97, 20, 86, 20, 86, 6, 60, 6, 60, 20, 73, 20, 73, 33, 60, 33, 60, 46, 86, 46, 86, 33, 94, 33, 92, 37, 90, 42, 88, 46, 86, 50, 83, 55, 80, 59, 77, 63, 74, 66, 71, 70, 67, 73, 63, 76, 60, 80, 60, 80, 60, 73, 67, 73, 69, 71, 73, 67, 73, 67, 73, 60, 33, 60, 33, 73, 46, 73, 46, 88, 42, 90, 37, 92, 33, 94, 33, 94, 33, 86, 20, 86, 20, 97, 16, 98, 11, 99, 6, 99, 6, 99, 6, 73, 20, 73, 20, 60, 6, 60, 6, 33, 20, 33, 20, 46, 46, 46, 46, 33, 33, 33, 33, 20, 46, 20, 46, 6, 20, 6, 20, 20, 6, 20, 6, 6, -7, 6, -7, 20, -20, 20, -20, 6, -47, 6, -47, 20, -34, 20, -34, 33, -47, 33, -47, 46, -20, 46, -20, 33, -7, 33, -7, 60, -20, 60, -20, 73, -7, 73, -7, 99, -11, 99, -16, 98, -20, 97, -20, 97, -20, 86, -34, 86, -34, 94, -38, 92, -43, 90, -47, 88, -47, 88, -47, 73, -34, 73, -34, 60, -74, 60, -74, 67, -72, 69, -68, 73, -68, 73, -60, 73, -60, 80, -64, 77, -68, 73, -71, 70, -75, 66, -78, 63, -81, 59, -84, 55, -86, 51, -89, 46, -91, 42, -93, 37, -95, 33, -95, 33, -87, 33, -87, 46, -60, 46, -60, 33, -74, 33, -74, 20, -60, 20, -60, 6, -87, 6, -87, 20, -98, 20, -99, 17, -100, 12, -100, 8, -100, 3, -100, -2, -100, -7, -100, -7, -87, -7, -87, -20, -98, -20, -98, -24, -96, -29, -95, -34, -93, -38, -91, -43, -89, -47, -89, -47, -87, -47, -87, -34, -74, -34, -74, -47, -60, -47, -60, -20, -74, -20, -74, -7, -47, -7, -47, -20, -34, -20, -34, -7, -7, -7, -7, -20, -20, -20, -20, -34, -7, -34, -7, -47, -34, -47, -34, -34, -47, -34, -47, -74, -34, -74, -34, -60, -7, -60, -7, -74, -20, -74, -20, -87, -7, -87, -7, -100, -10, -100, -15, -99, -20, -99, -24, -98, -29, -96, -34, -95, -34, -95, -34, -87, -47, -87, -47, -89, -48, -88, -52, -86, -57, -83, -60, -80, -60, -80, -60, -60, -74, -60, -74, -68, -71, -71, -71, -71};


// rot speed:inOut speed numbers 1 to 9 1= fast, 9 = slow
//motor_ratios(9,1);


void spirographWithSquare(float s, float d) // s = Side length of the square, d = Offset from the center to the drawing tip
{
    int numPoints = 300; // Number of points for the spirograph
    float R = 90; // Radius of the fixed circle

    drawing = 1; // Start drawing

    for (int i = 0; i < numPoints; i++) {
        // Calculate the angle for this point
        float angle = 20 * PI * i / numPoints;

        // Calculate the radius of the path traced by the center of the square
        float r = (R - s / 2); // Center of the square will move along a circle of radius (R - s / 2)

        // Position of the square's center
        float cx = r * cos(angle);
        float cy = r * sin(angle);

        // Calculate the angle of rotation of the square
        float rotationAngle = (R / s) * angle;

        // Calculate the position of the drawing tip on the square
        float tx = d * cos(rotationAngle) - (s / 2) * sin(rotationAngle);
        float ty = d * sin(rotationAngle) + (s / 2) * cos(rotationAngle);

        // Offset from the center of the square
        float x = cx + tx;
        float y = cy + ty;

        // Move the drawing arm to the calculated coordinates
        read_pots();
        gotoXY(x, y);
    }

    drawing = 0; // Stop drawing

    // Add a delay before starting the next pattern if needed
    // delay(100);
}

void spirograph (float r,float a)// r=Radius of the rolling circle, a= Distance from the center of the rolling circle
{
//const int radius = 100; // Radius of the circle
int numPoints = 300; // Number of points for the spirograph
float R = 80; // Radius of the fixed circle
 
 
 for (int i = 0; i < numPoints; i++) {
    // Parameters for the spirograph pattern
    // Calculate the angle for this point
   drawing=1;
    float angle = 6 * PI * i / numPoints;

    // Calculate the spirograph coordinates
    int x = (R - r) * cos(angle) + a * cos(((R - r) / r) * angle);
    int y = (R - r) * sin(angle) - a * sin(((R - r) / r) * angle);


// Calculate the epicycloid coordinates
   // int x = (R + r) * cos(angle) - a * cos(((R + r) / r) * angle);
   // int y = (R + r) * sin(angle) - a * sin(((R + r) / r) * angle);


       // Move the drawing arm to the calculated coordinates
        read_pots();
    gotoXY(x, y);
   

  }

drawing=0;
  // Add a delay before starting the next pattern
 // delay(100);

}


void motor_ratios(int r, int t) //r=rotation speed, // t= arm speed
{
 
  //int speed_rot_map[] = { 0, 9, 4, 2, 1, 1,1, 1 };
//int speed_InOut_map[] = { 0, 1, 1, 1, 1,7, 8, 9 };  

  x=0;
  y=0;
   digitalWrite(dirPin_rot, HIGH);  // CW rotation
  read_pots();
   knobs=1;
   //moving=0;
   //drawing=0;
   inOut = 1;  // 1=OUT, 0=IN
 ccwCW = 1;  // 0=CCW 1=CW
 
 rotON = 1;
  inOutON = 1;
  //moving=1;
  //turn on tracking
  //moveTo(0,0);

  speed_rot = r * speed_delay;
    speed_InOut = t * speed_delay;
     count = 0;
 //display();
 //delay(3000);


while (rotON == 1||inOutON == 1)
{
 read_pots();
   speed_rot = r * speed_delay;
    speed_InOut = t * speed_delay;
     
 display();
 calculate_xy(); // keep track of the location as it moves

 if ((r-t == 8 && count == 34) ||
    (r-t == 7 && count == 30) ||  // Assuming the pattern decreases count by 4
    (r-t == 6 && count == 26) ||  // Adjust the count values as needed
    (r-t == 5 && count == 22) ||
    (r-t == 4 && count == 18) ||
    (r-t == 3 && count == 15) ||
    (r-t == 2 && count == 10) ||
    (r-t == 1 && count == 6)||
    (r-t == 0 && count == 23)||
    (r-t < 0 && count == 1))
{
    rotON = 0;
    inOutON = 0;
    knobs = 0;
    count = 0;
    // moving = 0;
}
 

}

   knobs=0;
   count = 0;


     

}

void calculate_xy()
{
  //if (moving == 1) {
  r = (inOutSteps / inOut_total_steps) * desired_scale;  // changing
  x = r * cos(radAngle);
  y = r * sin(radAngle);
  //}
}

void clear_from_out()
{
  moveTo(0,100);
knobs=1;
ccwCW=0;
digitalWrite(dirPin_rot, ccwCW);
//inOutSteps=4300;
//ratio_select = 7;
 //  speed_rot= 9 * speed_delay;
//speed_InOut=1 * speed_delay;

//ratio_select = 0;

 rotON = 1;
  inOutON = 1;
  while (inOutSteps > 1)
  {
    display();
    read_pots();
    speed_rot= 1 * speed_delay;
speed_InOut=9 * speed_delay;
    calculate_xy();
  }
  knobs=0;
   rotON = 0;
  inOutON = 0;

}


void clear_from_in()
{
 moveTo(0,0);
 motor_ratios(1,9);

}


// HOMING JUST MOVED IN AS FAR AS POSSIBLE NO SENSOR NEEDED
void homing() {
  digitalWrite(enPin_InOut, LOW);   // Enable the stepper motor
  digitalWrite(dirPin_InOut, 1);    // Set direction to go in
  for (int x = 0; x < 4300; x++) {  // BLIND - DON'T KNOW HOW FAR TO GO IN GO IN TOTAL STEPS = 0,0
    digitalWrite(stepPin_InOut, HIGH);
    delayMicroseconds(100);
    digitalWrite(stepPin_InOut, LOW);
    delayMicroseconds(100);
  }
  inOut = 1;
  inOutON = 1;


  digitalWrite(dirPin_InOut, 0);  // Set direction TO OUT
}

void plotShape(int shape[], int size) {
drawing=1;
  for (int t = 0; t < (size / 2); t++)  // size of array div 2.
  {
    // if (button > 0) { return; }
    x2 = shape[t * 2];
    y2 = shape[t * 2 + 1];
    Serial.print(x2);
    Serial.print(",");
    Serial.println(y2);
    gotoXY(x2, y2);  //shift data left and down 50 units to make gride x100 by y100
  }
  drawing=0;// finished drawing patturn
}

// go to a specific point using iterpolation
void gotoXY(float targetX, float targetY) {
  // Calculate the difference between current and target positions
  float dx = targetX - x;
  float dy = targetY - y;

  // Calculate the total distance to travel
  float distance = sqrt(dx * dx + dy * dy);

  // Calculate the number of steps required
  int steps = (int)(distance * 1);  // Adjust stepsPerUnit according to your system

  // Ensure at least one step to move
  if (steps == 0) steps = 1;

  // Calculate the increments per step
  float incrementX = dx / steps;
  float incrementY = dy / steps;

  for (int i = 0; i <= steps; i++) {
    // Calculate the intermediate positions
    float intermediateX = x + incrementX * i;
    float intermediateY = y + incrementY * i;

    // Move the motors to the intermediate positions
    moveTo(intermediateX, intermediateY);
  }

  // Update the current position
  x = targetX;
  y = targetY;
}


// move to a point without interpolation.
void moveTo(float x4, float y4) {

  //digitalWrite(enPin_InOut, HIGH);   // Enable the stepper motor
  x2 = x4;
  y2 = y4;
  reverseKinematics(x4, y4);

  while (rotON == 1 || inOutON == 1) {
    // DO NOTHING!!! wait until hits rot Target location.
    //display();
    read_pots();
    delay(1);
  }


  //display();
  //delay(500);
}


void display() {

  if (moving == 1) calculate_xy();
 
  Serial.print(count);
  Serial.print(" \ ");
   Serial.print(knobs);
  Serial.print(" \ ");
 
    Serial.print(speed_rot);
  Serial.print(",");
  Serial.print(speed_InOut);
  Serial.print(",");


  Serial.print(offset);
  Serial.print(" *  ");
  Serial.print(inOutON);
  Serial.print(",");
  Serial.print(rotON);
  Serial.print(" *  ");
 

  Serial.print(x);
  Serial.print("x,y");
  Serial.print(y);
  Serial.print(" ");

  Serial.print(rotSteps);
  Serial.print(">");

  Serial.print(rotStepsTo);
  Serial.print(" ");
  Serial.print(inOutSteps);
  Serial.print(">");

  Serial.print(inOutStepsTo);
  // Serial.print(" (");
  //Serial.print(x2);
  //Serial.print(",");
  //Serial.print(y2);
  Serial.println("");
}

// Function to calculate reverse kinematics
void reverseKinematics(float xx, float yy) {
  // Calculate radial distance (r)
  rTo = sqrt(xx * xx + yy * yy);
  //rTo=rTo* (rot_total_steps/desired_scale);
  // Calculate angle (theta) using atan2
  // atan2 returns the angle in radians in the range -pi to pi
  angleTo = atan2(yy, xx);
  if (angleTo < 0) angleTo = (2 * PI) + angleTo;  // if neg

  //rotAngle=(ccwCW_Steps/16000.0)*360.0;
  rotStepsTo = (angleTo / (2 * PI)) * rot_total_steps;
  if (rotStepsTo == rot_total_steps) rotStepsTo = 0;
  inOutStepsTo = (rTo / desired_scale) * inOut_total_steps;
  if (inOutStepsTo > inOut_total_steps) inOutStepsTo = inOut_total_steps;  // in case point is outside circle.

  //(turn on the direction )
  if (inOutSteps != inOutStepsTo) inOutON = 1;
  if (rotSteps != rotStepsTo) rotON = 1;

  // Determine how to go IN or OUT?
  if (inOutSteps == inOutStepsTo) { inOutON = 0; }
  if (inOutSteps > inOutStepsTo) {
    inOut = 0;
    digitalWrite(dirPin_InOut, !inOut);
  }  // go in
  if (inOutSteps < inOutStepsTo) {
    inOut = 1;
    digitalWrite(dirPin_InOut, !inOut);
  }  // go out

  // determine if to go CW or CCW
  if ((rotStepsTo > rotSteps && (rotStepsTo - rotSteps < rot_total_steps / 2)) || (rotStepsTo < rotSteps && (rotSteps - rotStepsTo > rot_total_steps / 2))) {
    ccwCW = 0;
    digitalWrite(dirPin_rot, ccwCW);  // Set direction
  } else {
    ccwCW = 1;
    digitalWrite(dirPin_rot, ccwCW);  // Set direction
  }
}

void read_pots() {
  knob1 = analogRead(A0);  // Pot on the left FOR BRIGHTNESS
  knob2 = analogRead(A1);  // Pot on the right FOR DRAWING speed
  brightness = map(knob1, 0, 1023, 255, 0);
  analogWrite(lights_pin, brightness);
 
 
  speed_delay = map(knob2, 0, 1023, 50, 2000);  // MIN SPEED IS 40MS FOR MOTORS TO WORK.
 
 
 
  if (moving== 1) {
     //ratio_select = 0;// same speed for both motors
    speed_rot = speed_delay;
    speed_InOut = speed_delay;
  }

 

if (drawing== 1) {
  speed_delay = map(knob2, 0, 1023, 100, 2000);  // MIN SPEED IS 40MS FOR MOTORS TO WORK.
    speed_rot = speed_delay;
    speed_InOut = speed_delay;
  }


if (knob2 == 1023) pause=1;

while (pause == 1) {
   
    digitalWrite(enPin_InOut, HIGH);  // BOTH MOTORS OFF
    digitalWrite(enPin_rot, HIGH);    //
    knob1 = analogRead(A0);                // Pot on the left Patturn
    knob2 = analogRead(A1);
    analogWrite(lights_pin, brightness);
    brightness = map(knob1, 0, 1023, 255, 0);
    if (knob2 < 1023)
    {
    pause=0;
    digitalWrite(enPin_InOut, LOW);  // BOTH MOTORS ON
    digitalWrite(enPin_rot, LOW);    //

    }

}

}


void clear_Left_right()
{
// Clear from left and right
for (int i = -95; i <= 95; i += 10) {
    int xt = sqrt(10000 - (i * i));  // Calculate xt based on circle equation
    gotoXY(xt, i);
    gotoXY(-xt, i);
    int newY = i + 5;
    if (newY <= 95) {
        // Move to -xt, newY
        gotoXY(-xt, newY);
        // Move back to xt, newY
        gotoXY(xt, newY);
    }
}
}

void setup() {


  //Setup Interupts
  TCCR1A = 0;  // Init Timer1A
  TCCR1B = 0;  // Init Timer1B
  //TCNT1 = 0;            // initialize counter value to 0
  TCCR1B |= B00000011;  // Prescaler = 64
  OCR1A = 1000;         // Timer Compare1A Register  // = 16000000 / (64 * 1000) - 1 (must be <65536)
  OCR1B = 1000;         // Timer Compare1B Register
  //TIMSK1 |= B00000110;  // Enable Timer COMPA(y)   +COMPB (x) Interrupts
  TIMSK1 |= B00000110;  // only use one interupt

  // Define pins as outputs
  pinMode(stepPin_rot, OUTPUT);
  pinMode(dirPin_rot, OUTPUT);
  pinMode(stepPin_InOut, OUTPUT);
  pinMode(dirPin_InOut, OUTPUT);
  pinMode(enPin_InOut, OUTPUT);
  pinMode(enPin_rot, OUTPUT);
  pinMode(lights_pin, OUTPUT);
  //pinMode(IR_Input, INPUT);
  //digitalWrite(lights_pin, HIGH);
  digitalWrite(enPin_InOut, LOW);  // BOTH MOTORS ON
  digitalWrite(enPin_rot, LOW);    //

  // Setting up pin change interrupt for the corresponding pin
  //  PCMSK0 |= (1 << (IR_Input - 8));
  //  PCICR |= (1 << PCIE0);  // Enable pin change interrupt for group 0
  //ir sensor

  Serial.begin(9600);
  Serial.println("sTARting ");
  digitalWrite(dirPin_InOut, 0);  // Set direction
  //digitalWrite(dirPin_rot, !ccwCW);  // Set direction
  display();
  homing();

 
digitalWrite(enPin_InOut, LOW);  // BOTH MOTORS ON
  digitalWrite(enPin_rot, LOW);    //
Serial.println("READY ");

  // if using the pots function turn both motors on.
  //if (knobs == 1) {
//    rotON = 1;
  //  inOutON = 1;
    //read_pots();


 rotON = 0;
inOutON = 0;

  digitalWrite(dirPin_rot, HIGH);  // CW rotation
  display();
}

void loop() {


// you can change the order of patterns


clear_from_in();
plotShape(maze, sizeof(maze) / sizeof(maze[0]));
clear_from_out();
motor_ratios(4,1); // star ratio
clear_Left_right();
plotShape(spiral, sizeof(spiral) / sizeof(spiral[0]));
clear_from_out();
plotShape(star, sizeof(star) / sizeof(star[0]));
clear_from_out();
motor_ratios(1,1); // 2 loops issues
clear_Left_right();
spirograph(15,30); //r,a
spirograph(15,35); //r,a


  //display if motors are moving

  if (rotON == 1 || inOutON == 1) { display(); }


 

  read_pots();


  //draw patturn after last sequence.
 


  // CAN ALSO SEND COMMANDS VIA USART
  if (Serial.available() > 0) {
    // Read the incoming byte
    char command = Serial.read();

    if (command == 'q') {  // turn on x motor
      rotON = !rotON;
      //if (inOutON==0) moving = !moving;

     
     moving = !moving;
      // digitalWrite(enPin_rot, !rotON);  // Enable the stepper motor
    }

    if (command == 'w') {               // change dir of x motor
      digitalWrite(dirPin_rot, ccwCW);  // Set direction
      ccwCW = !ccwCW;
    }


    if (command == 'e') {  // change dir of x motor

      speed_rot += 50;
      //digitalWrite(dirPin_rot,ccwCW); // Set direction
      // ccwCW=!ccwCW;
    }
    if (command == 'r') {  // change dir of x motor

      speed_rot -= 50;
      //digitalWrite(dirPin_rot,ccwCW); // Set direction
      // ccwCW=!ccwCW;
    }
    if (command == 'a') {  // turn on x motor
      inOutON = !inOutON;
      //if (rotON==0)moving = !moving;
     moving = !moving;
      // digitalWrite(enPin_InOut, !inOutON);  // Enable the stepper motor
    }

    if (command == 's') {                 // change dir of x motor
      digitalWrite(dirPin_InOut, inOut);  // Set direction
      inOut = !inOut;
    }

    if (command == 'd') {  // change dir of x motor

      speed_InOut -= 50;
      //digitalWrite(dirPin_rot,ccwCW); // Set direction
      // ccwCW=!ccwCW;
    }
    if (command == 'f') {  // change dir of x motor

      speed_InOut += 50;
      //digitalWrite(dirPin_rot,ccwCW); // Set direction
      // ccwCW=!ccwCW;
    }


    if (command == '1') {
      knobs = !knobs;

      inOutON = 1;
      rotON = 1;

     
    }
    if (command == '2') {

     
      clear_from_in();
    }

     if (command == '3') {

     
      clear_from_out();
    }

 if (command == '4') {

}

if (command == '5') {
     plotShape(star, sizeof(star) / sizeof(star[0]));
}


    if (command == 'x') {
     

      plotShape(maze, sizeof(maze) / sizeof(maze[0]));
    }
    if (command == 'c') {
   

    clear_Left_right();

    }

if (command == 'v') {
     plotShape(spiral, sizeof(spiral) / sizeof(spiral[0]));
}

    if (command == 'z') {
     

     

clear_from_in();
motor_ratios(1,3);
clear_from_out();

     

     
    }


   
  }
}


//IR SENSOR REMOTE CONTROL
ISR(PCINT0_vect) {
  // When the pin goes HIGH record the pulse start time
  if (digitalRead(IR_Input) == HIGH) {
    Start_Time = micros();
  } else if (Start_Time != 0) {  // Pin went LOW
    // Calculate the pulse width
    Pulse_Width = micros() - Start_Time;
    // Clear the timer
    Start_Time = 0;

    if (Pulse_Width > Sync_Time) {
      // Sync bit
      Bit_Count = 0;
      Byte_Count = 0;
    } else if (Pulse_Width > One_Time) {
      // Data bit = 1
      bitSet(IR_Bytes[Byte_Count], Bit_Count);
      Bit_Count++;
    } else if (Pulse_Width > Zero_Time) {
      // Data bit = 0
      bitClear(IR_Bytes[Byte_Count], Bit_Count);
      Bit_Count++;
    } else {
      Bit_Count = 0;  // Error
    }

    if (Bit_Count == 8) {  // 8 bits to a byte
      Byte_Ready = true;
      Bit_Count = 0;
      Byte_Count++;
    }

    if (Byte_Count == 4) {
      Byte_Count = 0;

      // Check for valid header
      if ((IR_Bytes[0] == 0x00) && (IR_Bytes[1] == 0xFF)) {
        // Decode command byte
        byte button = IR_Bytes[2];
        // Serial.print("button="); // print the value of the button if you use a different controller.
        // Serial.println(button);
        if (button == 24) brightness = brightness + 10;  //up
        if (button == 82) brightness = brightness - 10;  // down
        if (button == 90) pause = !pause;                // right
        if (button == 8) pause = !pause;                 // left

        if (button == 28) pause = !pause;                // ok button
        if (button == 22) brightness = brightness - 10;  // *
        if (button == 13) brightness = brightness + 10;  // #

        // Numbers
     
        if (button == 21) pause = !pause;    // 8
        if (button == 9) pause = !pause;     // 9
        if (button == 25) pause = !pause;    // 0
      }
    }
  }
}

// ISR for the rotational motor
ISR(TIMER1_COMPA_vect) {
  OCR1A += speed_rot;  // Advance the COMPA register

  if (rotON == 1) {
    pwm = !pwm;
    digitalWrite(stepPin_rot, pwm);  // ONE STEP OF ROT MOTOR

    //uncloment later
    if (offset == 0 && inOutON == 0 && rotON == 1) {  // If only Rot motor is on composinate for inout motor direction is opposite rot direction
      digitalWrite(stepPin_InOut, HIGH);
      digitalWrite(dirPin_InOut, !ccwCW);  // Set direction opposite rot motor
    }

    if (pwm == 0) {
      offset += 1;  // track each step of rot

      if (ccwCW == 1) {
        rotSteps -= 1;
        //digitalWrite(dirPin_rot, HIGH);
        if (offset >= 10) {  //after 10 steps compinstate for inout motor
          offset = 0;
          if (inOutON == 1 && rotON == 1) inOutSteps -= 1;
          if (inOutON == 0 && rotON == 1) digitalWrite(stepPin_InOut, LOW);
        }

      } else {
        rotSteps += 1;
        //  digitalWrite(dirPin_rot, LOW);
        if (offset >= 10) {
          offset = 0;
          if (inOutON == 1 && rotON == 1) inOutSteps += 1;
          if (inOutON == 0 && rotON == 1) digitalWrite(stepPin_InOut, LOW);
        }
      }

      // Adjust the direction if limits are reached (note sure if needed?)
      if (inOutSteps > inOut_total_steps) {
        inOut = 0;
        digitalWrite(dirPin_InOut, HIGH);  // Set direction
      } else if (inOutSteps < 0) {
        inOut = 1;
        digitalWrite(dirPin_InOut, LOW);  // Set direction
      }


      // Wrap-around conditions for rotational steps
      if (rotSteps > rot_total_steps) rotSteps = 0;
      else if (rotSteps < 0) rotSteps = rot_total_steps - 1;

      radAngle = (rotSteps / rot_total_steps) * (2 * PI);  // Update angles
      rotAngle = (rotSteps / rot_total_steps) * 360.0;


      if (rotSteps == rotStepsTo && knobs == 0&&moving==0&&clearing==0) { rotON = 0; }  // turn off motor if it reaches the set point

      if (inOutSteps == inOutStepsTo && knobs == 0&&moving==0&&clearing==0) inOutON = 0;
    }
  }
}

// ISR for the in/out motor
ISR(TIMER1_COMPB_vect) {
  OCR1B += speed_InOut;  // Advance the COMPB register

  if (inOutON == 1) {
    pwm2 = !pwm2;


    if (inOut == 0) digitalWrite(dirPin_InOut, HIGH);  // Set direction
    if (inOut == 1) digitalWrite(dirPin_InOut, LOW);   // Set direction

    digitalWrite(stepPin_InOut, pwm2);

    if (pwm2 == 0) {
      // Set the direction based on the target steps

      // Update in/out steps based on the direction
      if (inOut == 1) inOutSteps++;
      else inOutSteps--;

      if (inOutSteps == inOutStepsTo && knobs == 0&&moving==0&&clearing==0) { inOutON = 0; }  //offset=0;


      // Adjust the direction if limits are reached
      if (inOutSteps > inOut_total_steps) {
        inOut = 0;
        digitalWrite(dirPin_InOut, HIGH);  // Set direction
      } else if (inOutSteps < 0) {
        inOut = 1;
        digitalWrite(dirPin_InOut, LOW);  // Set direction
      }
      if (knobs == 1) {

        if (state != inOut) count += 1;
        state = inOut;
      }  


       
    }
  }
}