Circular Servo Motor Array with Arduino Mega 2560 – Kinetic Art & Robotic Display System (Wokwi Simulation)
Kinetic Mandala — Circular Servo Array Using Arduino Mega 2560
Control 32 servo motors arranged in a perfect radial pattern from a single Arduino Mega. Create wave animations, compass sweeps, and synchronized kinetic art. Fully simulated in Wokwi — no hardware needed to start.
32 SG90 servo motors arranged in a perfect radial pattern, individually controlled via PWM pins 22–53
🎯 Project Overview
The Circular Servo Motor Array uses 32 SG90-type micro servo motors arranged in a perfect circle, each controlled individually by the Arduino Mega 2560's digital I/O pins 22–53. Designed and simulated in Wokwi, this project is ideal for kinetic sculptures, mechanical displays, and interactive art systems.
🔩 Hardware Components
🔌 Wiring Guide
Each servo has 3 wires. Connect them as follows — repeat for all 32 servos:
| Servo Wire | Colour | Connect To | Notes |
|---|---|---|---|
| Signal (PWM) | Orange/Yellow | Arduino pin 22–53 (servo 0–31) | Servo 0 → Pin 22, Servo 1 → Pin 23 … |
| Power (V+) | Red | External 5V Power Rail | Never use Arduino 5V for all 32 |
| Ground | Brown/Black | Common GND rail | Connect to both PSU GND and Arduino GND |
📐 Step-by-Step Instructions
GenerateDiagram() function in the sketch can regenerate this JSON fresh via Serial output — call it from setup() and copy the Serial Monitor output.NUM_SERVOS 32— number of motorsServo myServo[NUM_SERVOS]— servo array- Attach loop using pins 22–53 in
setup() - 4 motion sequences in
loop()
- Random chaos (15× repetitions)
- Unified sweep 0°↔180° (3× repetitions)
- Rotating wave effect (6× repetitions)
- Compass pointer animation
- Mount 32 SG90 servos in a circular frame (laser-cut acrylic or 3D-printed ring works well)
- Connect each PWM signal wire to Arduino pin
i + 22 - Route all V+ wires to a power distribution bus connected to an external 5V / 20A+ supply
- Connect all GND wires together and to the Arduino GND
- Upload the sketch via USB using the Arduino IDE
- Change
NUM_SERVOSto 16, 24, or 40 (adjustdiagram.jsonaccordingly) - Adjust wave speed by changing the
delay(40)in Sequence 3 - Modify the angle formula
90 - (10 * d)to control wave steepness - Add a sine wave pattern using
sin()frommath.h
🎮 Try It Free in Your Browser
No hardware needed — run the full 32-servo simulation instantly in Wokwi. Watch all 4 motion sequences live.
Launch Free Simulation on Wokwi🌊 Motion Sequences — How They Work
The loop() function cycles through 4 sequences automatically. Here's the maths behind each one:
Calls random(0, 181) for each servo. 15 rounds, with a 150ms pause between each. Simple but visually striking — useful for "alive" organic art effects.
Increments a shared angle r from 0→180 then 180→0, writing it to all 32 servos simultaneously. 6ms delay per degree = a smooth, unified sweep.
For each "active" servo i, each other servo j gets an angle based on its circular distance: angle = 90 - (10 × d) where d uses modular arithmetic to wrap around the ring. Creates a smooth ripple.
The showPointer(s) function uses the % (modulo) operator to wrap servo indices. Servo s and s+1 form the pointer tip; s+16 and s+17 (opposite side) form the tail. All others lay flat.
💻 Arduino Code (sketch.ino)
Copy this complete sketch into your Wokwi sketch.ino tab or the Arduino IDE:
// ServoOverdone.ino
// Circular servo motor array — 32 SG90 servos on Arduino Mega 2560
// Version 2, August 2021 — Public Domain
#include <Servo.h>
#define NUM_SERVOS 32
Servo myServo[NUM_SERVOS];
void setup()
{
// Attach pins 22–53 to servo objects (32 pins)
for( int i=0; i<NUM_SERVOS; i++)
{
myServo[i].attach( i + 22);
}
}
void loop()
{
// ── Sequence 1: Random angles (15 rounds) ──
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 2: Synchronized sweep 0°↔180° (3 repetitions) ──
for( int i=0; i<NUM_SERVOS; i++)
myServo[i].write( 0);
delay( 1000);
for( int a=0; a<3; a++)
{
for( int r=0; r<=180; r++) // sweep right
{
for( int i=0; i<NUM_SERVOS; i++)
myServo[i].write( r);
delay( 6);
}
for( int r=180; r>=0; r--) // sweep left
{
for( int i=0; i<NUM_SERVOS; i++)
myServo[i].write( r);
delay( 6);
}
}
// ── Sequence 3: Rotating wave (6 full rotations) ──
for( int a=0; a<6; a++)
{
for( int i=0; i<NUM_SERVOS; i++)
{
for( int j=0; j<NUM_SERVOS; j++)
{
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 4: Compass pointer ──
int pointer = NUM_SERVOS * 3 / 4; // start pointing up
showPointer( pointer);
delay( 1000);
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);
showPointer( ++pointer);
delay(2000);
}
// Creates a compass pointer using servo at position 's'
// Uses modulo (%) to wrap around the circular array
void showPointer( int s)
{
int pointerA = s % NUM_SERVOS;
int pointerB = (s + 1) % NUM_SERVOS;
int tailA = (s + 16) % NUM_SERVOS;
int tailB = (s + 17) % NUM_SERVOS;
myServo[pointerA].write(180-56); // = 124°
myServo[pointerB].write(56);
myServo[tailA].write(95);
myServo[tailB].write(85);
int n = (NUM_SERVOS / 2) - 2;
int start = pointerB + 1;
for( int i=0; i<n; i++)
myServo[(start + i) % NUM_SERVOS].write( 2);
start = tailB + 1;
for( int i=0; i<n; i++)
myServo[(start + i) % NUM_SERVOS].write( 178);
}
📄 diagram.json — Wokwi Layout
Paste this into the diagram.json tab in Wokwi. It places all 32 servos in a perfect radial circle around the Arduino Mega, with red horn colours:
{
"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", "Green", [] ],
[ "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": {}
}
⚡ Power Planning
32 servos demand serious current. Here's the calculation:
🚀 Possible Applications
🎓 What You Will Learn
📚 Related Projects on MakeMindz
Ready to Build? Start with the Free Simulation
All 32 servos, all 4 motion sequences, live in your browser — no components, no soldering required.
Open Free Simulation →
Comments
Post a Comment