Kinetic mandala using servo motors and arduino mega with step by step guide

 This appears to be a circular servo motor array - approximately 36-40 servo motors controlled by a single Arduino Mega 2560, creating what could be a kinetic sculpture, LED display mechanism, or robotic platform.



Step-by-Step Description

1. Hardware Components

  • Arduino Mega 2560 - Central controller (needed because it has many PWM pins)
  • ~36-40 Servo Motors - Arranged in a circle, likely SG90 or similar hobby servos
  • Power Distribution - The red components appear to be power rails/buses
  • Wiring - Multiple colored wires (signal, power, ground)


2. Wiring Architecture

  • Signal Wires (various colors) - Connect from Arduino digital pins to each servo's signal pin
  • Power Rails (red) - Distribute 5V to all servos (servos typically need external power, not from Arduino)
  • Ground (black/brown) - Common ground between Arduino and servos
  • Control Pins - Each servo uses one digital pin from the Arduino Mega


3. Circuit Design Pattern

The servos are arranged in a radial pattern where:

  • Each servo gets its own control signal from the Arduino
  • Power is shared through a common power distribution system
  • Grounds are connected together (common ground principle)

4. Programming 

Servoarduino ino:

// ServoOverdone.ino
//
// Example for multiple Servo objects in a array.
//
// Version 1, 28 July 2021, by Koepel.
// Version 2, 15 August 2021, by Koepel.
//   changed timing, a little slower
//   diagram.json has servos in reverse order (I think it is visually better)
//   Added fourth sequence: "compass"
//
// Public Domain
//

#include <Servo.h>

#define NUM_SERVOS 32
Servo myServo[NUM_SERVOS];

void setup()
{
  // Attach pins from the Arduino Mega board to the Servo objects.
  // Starting from pin 22, there happen to be exactly 32 pins on the double row pins.
  for( int i=0; i<NUM_SERVOS; i++)
  {
    myServo[i].attach( i + 22);      // pin 22 up to 53 is 32 pins
  }
}

void loop()
{
  // Sequence one.
  // All servo motor are set to a random angle.
  for( int a=0; a<15; a++)
  {
    for( int i=0; i<NUM_SERVOS; i++)
    {
      myServo[i].write( random( 0, 181));
      delay( 2);
    }
    delay( 150);
  }

  // Sequence two.
  // All servo motors move with the same angle.
  for( int i=0; i<NUM_SERVOS; i++)
  {
    myServo[i].write( 0);            // set to begin position (horn is rotated left)
  }
  delay( 1000);                      // wait to let the viewer get used to it

  for( int a=0; a<3; a++)
  {
    for( int r=0; r<=180; r++)       // move horns to the right
    {
      for( int i=0; i<NUM_SERVOS; i++)
      {
        myServo[i].write( r);
      }
      delay( 6);
    }
    for( int r=180; r>=0; r--)
    {
      for( int i=0; i<NUM_SERVOS; i++)  // move horns to the left
      {
        myServo[i].write( r);
      }
      delay( 6);
    }
  }

  // Sequence three.
  // A rotating wave.
  for( int a=0; a<6; a++)
  {
    for( int i=0; i<NUM_SERVOS; i++)
    {
      for( int j=0; j<NUM_SERVOS; j++)
      {
        // Calculate distance to active servo
        int d = j - i;
        if( d < 0)
          d = -d;
        if( d > (NUM_SERVOS / 2))
          d = NUM_SERVOS - d;

        int angle = 90 - (10 * d);
        if( angle < 0)
          angle = 0;
        myServo[j].write( angle);
      }
      delay(40);
    }
  }

  // Sequence four.
  // A "compass"
  // Start by pointing upwards
  int pointer = NUM_SERVOS * 3 / 4;
  showPointer( pointer);
  delay( 1000);                       // let the viewer get used to new pattern

  for( int i=0; i<5; i++)
  {
    showPointer( --pointer);
    delay( 150);
  }
  delay( 200);
  for( int i=0; i<9; i++)
  {
    showPointer( ++pointer);
    delay( 150);
  }
  delay( 200);
  for( int i=0; i<5; i++)
  {
    showPointer( --pointer);
    delay( 150);
  }
  delay( 200);
  for( int i=0; i<4; i++)
  {
    showPointer( ++pointer);
    delay( 150);
  }
  delay( 160);
  for( int i=0; i<2; i++)
  {
    showPointer( --pointer);
    delay( 150);
  }
  delay( 80);
  for( int i=0; i<1; i++)
  {
    showPointer( ++pointer);
    delay( 150);
  }

  delay( 2000);
}

// This function makes a "pointer" with the servos.
// It is used to create the "compass".
// The parameter 's' is the servo motor that has the pointer.
// It is allowed that 's' is below zero or larger than the numbers of servo motors.
void showPointer( int s)
{
  int pointerA = s % NUM_SERVOS;        // Using the '%' (remainder) for valid number
  int pointerB = (s + 1) % NUM_SERVOS;  // pointer is made with the next servo motor
  int tailA = (s + 16) % NUM_SERVOS;
  int tailB = (s + 17) % NUM_SERVOS;

  // make pointer with servo motor s and s+1.
  myServo[pointerA].write(180-56);
  myServo[pointerB].write(56);

  // make tail with servo motor s+16 and s+17.
  myServo[tailA].write(95);
  myServo[tailB].write(85);

  // Set servos right of pointer
  int n = (NUM_SERVOS / 2) - 2;
  int start = pointerB + 1;
  for( int i=0; i<n; i++)
  {
    int j = (start + i) % NUM_SERVOS;
    myServo[j].write( 2);
  }

  // Set servos left of pointer
  start = tailB + 1;
  for( int i=0; i<n; i++)
  {
    int j = (start + i) % NUM_SERVOS;
    myServo[j].write( 178);
  }
}

// The function GenerateDiagram() can be used to generate
// the diagram.json file for Wokwi.
// To use it, call it from the setup() function, and the
// serial output can be copied into the diagram.json file.
void GenerateDiagram()
{
  Serial.begin(115200);

  Serial.print( "{\n");
  Serial.print( "  \"version\": 1,\n");
  Serial.print( "  \"author\": \"Generated\",\n");
  Serial.print( "  \"editor\": \"wokwi\",\n");
  Serial.print( "  \"parts\": [\n");

  Serial.print( "    {\n");
  Serial.print( "      \"type\": \"wokwi-arduino-mega\",\n");
  Serial.print( "      \"id\": \"mega\",\n");
  Serial.print( "      \"top\": 270,\n");
  Serial.print( "      \"left\": 185,\n");
  Serial.print( "      \"attrs\": {}\n");
  Serial.print( "    },\n");

  // Put the servo motor in reverse order in the diagram.json
  // I think that is visually better.
  // The horn now overlaps the next servo when the horn moves to the right.
  for( int i=NUM_SERVOS-1; i>=0; i--)
  {
    float rotate = float( i) * (360.0 / float( NUM_SERVOS));
    float rad = rotate / 360.0 * 2.0 * M_PI;
    float top = (300.0 * sin( rad)) + 300.0;
    float left = (300.0 * cos( rad)) + 300.0;
    Serial.print( "    {\n");
    Serial.print( "      \"type\": \"wokwi-servo\",\n");
    Serial.print( "      \"id\": \"servo");
    Serial.print( i);
    Serial.print( "\",\n");
    Serial.print( "      \"top\": ");
    Serial.print( top);
    Serial.print( ",\n");
    Serial.print( "      \"left\": ");
    Serial.print( left);
    Serial.print( ",\n");
    Serial.print( "      \"rotate\": ");
    Serial.print( rotate);
    Serial.print( ",\n");
    Serial.print( "      \"attrs\": { \"hornColor\": \"Red\" }\n");
    Serial.print( "    }");
    if( i != 0)
      Serial.print( ",");
    Serial.print( "\n");
  }

  Serial.print( "  ],\n");
  Serial.print( "  \"connections\": [\n");

  for( int i=0; i<NUM_SERVOS; i++)
  {
    int j = i + 1;
    if( j == NUM_SERVOS)
      j = 0;
    Serial.print( "    [ \"servo");
    Serial.print( i);
    Serial.print( ":V+\", \"servo");
    Serial.print( j);
    Serial.print( ":V+\", \"Red\", [] ],\n");
    Serial.print( "    [ \"servo");
    Serial.print( i);
    Serial.print( ":GND\", \"servo");
    Serial.print( j);
    Serial.print( ":GND\", \"Black\", [] ],\n");

    Serial.print( "    [ \"mega:");
    Serial.print( i + 22);
    Serial.print( "\", \"servo");
    Serial.print( i);
    Serial.print( ":PWM\", \"Green\", [ ] ],\n");
  }
  Serial.print( "    [ \"mega:GND.2\", \"servo9:GND\", \"Black\", [ ] ],\n");
  Serial.print( "    [ \"mega:5V\", \"servo9:V+\", \"Red\", [ ] ]\n");

  Serial.print( "  ]\n");
  Serial.print( "}\n");
}

5. Possible Applications

  • Kinetic art installation - Creating wave or ripple effects
  • LED clock/display - Each servo could point to create patterns
  • Mechanical display - Flip-dot style visual display
  • Robot base - Omnidirectional movement platform
  • Educational demonstration - Teaching synchronized motor control
DIAGRAM .json:

{
  "version": 1,
  "author": "Generated",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-mega", "id": "mega", "top": 270, "left": 185, "attrs": {} },
    {
      "type": "wokwi-servo",
      "id": "servo31",
      "top": 241.47,
      "left": 594.24,
      "rotate": 348.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo30",
      "top": 185.19,
      "left": 577.16,
      "rotate": 337.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo29",
      "top": 133.33,
      "left": 549.44,
      "rotate": 326.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo28",
      "top": 87.87,
      "left": 512.13,
      "rotate": 315,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo27",
      "top": 50.56,
      "left": 466.67,
      "rotate": 303.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo26",
      "top": 22.84,
      "left": 414.81,
      "rotate": 292.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo25",
      "top": 5.76,
      "left": 358.53,
      "rotate": 281.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo24",
      "top": 0,
      "left": 300,
      "rotate": 270,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo23",
      "top": 5.76,
      "left": 241.47,
      "rotate": 258.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo22",
      "top": 22.84,
      "left": 185.2,
      "rotate": 247.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo21",
      "top": 50.56,
      "left": 133.33,
      "rotate": 236.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo20",
      "top": 87.87,
      "left": 87.87,
      "rotate": 225,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo19",
      "top": 133.33,
      "left": 50.56,
      "rotate": 213.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo18",
      "top": 185.19,
      "left": 22.84,
      "rotate": 202.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo17",
      "top": 241.47,
      "left": 5.76,
      "rotate": 191.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo16",
      "top": 300,
      "left": 0,
      "rotate": 180,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo15",
      "top": 358.53,
      "left": 5.76,
      "rotate": 168.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo14",
      "top": 414.8,
      "left": 22.84,
      "rotate": 157.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo13",
      "top": 466.67,
      "left": 50.56,
      "rotate": 146.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo12",
      "top": 512.13,
      "left": 87.87,
      "rotate": 135,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo11",
      "top": 549.44,
      "left": 133.33,
      "rotate": 123.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo10",
      "top": 577.16,
      "left": 185.19,
      "rotate": 112.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo9",
      "top": 594.24,
      "left": 241.47,
      "rotate": 101.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo8",
      "top": 600,
      "left": 300,
      "rotate": 90,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo7",
      "top": 594.24,
      "left": 358.53,
      "rotate": 78.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo6",
      "top": 577.16,
      "left": 414.81,
      "rotate": 67.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo5",
      "top": 549.44,
      "left": 466.67,
      "rotate": 56.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo4",
      "top": 512.13,
      "left": 512.13,
      "rotate": 45,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo3",
      "top": 466.67,
      "left": 549.44,
      "rotate": 33.75,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo2",
      "top": 414.81,
      "left": 577.16,
      "rotate": 22.5,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo1",
      "top": 358.53,
      "left": 594.24,
      "rotate": 11.25,
      "attrs": { "hornColor": "Red" }
    },
    {
      "type": "wokwi-servo",
      "id": "servo0",
      "top": 300,
      "left": 600,
      "attrs": { "hornColor": "Red" }
    }
  ],
  "connections": [
    [ "servo0:V+", "servo1:V+", "Red", [] ],
    [ "servo0:GND", "servo1:GND", "Black", [] ],
    [ "mega:22", "servo0:PWM", "Green", [] ],
    [ "servo1:V+", "servo2:V+", "Red", [] ],
    [ "servo1:GND", "servo2:GND", "Black", [] ],
    [ "mega:23", "servo1:PWM", "Green", [] ],
    [ "servo2:V+", "servo3:V+", "Red", [] ],
    [ "servo2:GND", "servo3:GND", "Black", [] ],
    [ "mega:24", "servo2:PWM", "Green", [] ],
    [ "servo3:V+", "servo4:V+", "Red", [] ],
    [ "servo3:GND", "servo4:GND", "Black", [] ],
    [ "mega:25", "servo3:PWM", "Green", [] ],
    [ "servo4:V+", "servo5:V+", "Red", [] ],
    [ "servo4:GND", "servo5:GND", "Black", [] ],
    [ "mega:26", "servo4:PWM", "Green", [] ],
    [ "servo5:V+", "servo6:V+", "Red", [] ],
    [ "servo5:GND", "servo6:GND", "Black", [] ],
    [ "mega:27", "servo5:PWM", "Green", [] ],
    [ "servo6:V+", "servo7:V+", "Red", [] ],
    [ "servo6:GND", "servo7:GND", "Black", [] ],
    [ "mega:28", "servo6:PWM", "Green", [] ],
    [ "servo7:V+", "servo8:V+", "Red", [] ],
    [ "servo7:GND", "servo8:GND", "Black", [] ],
    [ "mega:29", "servo7:PWM", "Green", [] ],
    [ "servo8:V+", "servo9:V+", "Red", [] ],
    [ "servo8:GND", "servo9:GND", "Black", [] ],
    [ "mega:30", "servo8:PWM", "Green", [] ],
    [ "servo9:V+", "servo10:V+", "Red", [] ],
    [ "servo9:GND", "servo10:GND", "Black", [] ],
    [ "mega:31", "servo9:PWM", "Green", [] ],
    [ "servo10:V+", "servo11:V+", "Red", [] ],
    [ "servo10:GND", "servo11:GND", "Black", [] ],
    [ "mega:32", "servo10:PWM", "Green", [] ],
    [ "servo11:V+", "servo12:V+", "Red", [] ],
    [ "servo11:GND", "servo12:GND", "Black", [] ],
    [ "mega:33", "servo11:PWM", "Green", [] ],
    [ "servo12:V+", "servo13:V+", "Red", [] ],
    [ "servo12:GND", "servo13:GND", "Black", [] ],
    [ "mega:34", "servo12:PWM", "Green", [] ],
    [ "servo13:V+", "servo14:V+", "Red", [] ],
    [ "servo13:GND", "servo14:GND", "Black", [] ],
    [ "mega:35", "servo13:PWM", "Green", [] ],
    [ "servo14:V+", "servo15:V+", "Red", [] ],
    [ "servo14:GND", "servo15:GND", "Black", [] ],
    [ "mega:36", "servo14:PWM", "Green", [] ],
    [ "servo15:V+", "servo16:V+", "Red", [] ],
    [ "servo15:GND", "servo16:GND", "Black", [] ],
    [ "mega:37", "servo15:PWM", "Green", [] ],
    [ "servo16:V+", "servo17:V+", "Red", [] ],
    [ "servo16:GND", "servo17:GND", "Black", [] ],
    [ "mega:38", "servo16:PWM", "Green", [] ],
    [ "servo17:V+", "servo18:V+", "Red", [] ],
    [ "servo17:GND", "servo18:GND", "Black", [] ],
    [ "mega:39", "servo17:PWM", "Green", [] ],
    [ "servo18:V+", "servo19:V+", "Red", [] ],
    [ "servo18:GND", "servo19:GND", "Black", [] ],
    [ "mega:40", "servo18:PWM", "Green", [] ],
    [ "servo19:V+", "servo20:V+", "Red", [] ],
    [ "servo19:GND", "servo20:GND", "Black", [] ],
    [ "mega:41", "servo19:PWM", "gold", [] ],
    [ "servo20:V+", "servo21:V+", "Red", [] ],
    [ "servo20:GND", "servo21:GND", "Black", [] ],
    [ "mega:42", "servo20:PWM", "Green", [] ],
    [ "servo21:V+", "servo22:V+", "Red", [] ],
    [ "servo21:GND", "servo22:GND", "Black", [] ],
    [ "mega:43", "servo21:PWM", "Green", [] ],
    [ "servo22:V+", "servo23:V+", "Red", [] ],
    [ "servo22:GND", "servo23:GND", "Black", [] ],
    [ "mega:44", "servo22:PWM", "Green", [] ],
    [ "servo23:V+", "servo24:V+", "Red", [] ],
    [ "servo23:GND", "servo24:GND", "Black", [] ],
    [ "mega:45", "servo23:PWM", "Green", [] ],
    [ "servo24:V+", "servo25:V+", "Red", [] ],
    [ "servo24:GND", "servo25:GND", "Black", [] ],
    [ "mega:46", "servo24:PWM", "Green", [] ],
    [ "servo25:V+", "servo26:V+", "Red", [] ],
    [ "servo25:GND", "servo26:GND", "Black", [] ],
    [ "mega:47", "servo25:PWM", "Green", [] ],
    [ "servo26:V+", "servo27:V+", "Red", [] ],
    [ "servo26:GND", "servo27:GND", "Black", [] ],
    [ "mega:48", "servo26:PWM", "Green", [] ],
    [ "servo27:V+", "servo28:V+", "Red", [] ],
    [ "servo27:GND", "servo28:GND", "Black", [] ],
    [ "mega:49", "servo27:PWM", "Green", [] ],
    [ "servo28:V+", "servo29:V+", "Red", [] ],
    [ "servo28:GND", "servo29:GND", "Black", [] ],
    [ "mega:50", "servo28:PWM", "Green", [] ],
    [ "servo29:V+", "servo30:V+", "Red", [] ],
    [ "servo29:GND", "servo30:GND", "Black", [] ],
    [ "mega:51", "servo29:PWM", "Green", [] ],
    [ "servo30:V+", "servo31:V+", "Red", [] ],
    [ "servo30:GND", "servo31:GND", "Black", [] ],
    [ "mega:52", "servo30:PWM", "Green", [] ],
    [ "servo31:V+", "servo0:V+", "Red", [] ],
    [ "servo31:GND", "servo0:GND", "Black", [] ],
    [ "mega:53", "servo31:PWM", "Green", [] ],
    [ "mega:GND.2", "servo9:GND", "Black", [] ],
    [ "mega:5V", "servo9:V+", "Red", [] ]
  ],
  "dependencies": {}
}

6. Key Considerations

  • Power Requirements - 36 servos can draw significant current (possibly 1-2A each under load = 36-72A total!), requiring a robust external power supply
  • Pin Limitations - Arduino Mega has 54 digital I/O pins, sufficient for this many servos
  • Timing - Servo library can handle multiple servos, but may need careful timing for smooth operation

Comments