Plentiful_Props_3D
CANADA • + Follow
Edit Project
Components
Description
Iron Man Repulsor Glove PCB (with laser)
This PCB will allow you to build your very own Iron Man repulsor glove, complete with movement based lights and sounds! for more details, ingluding the materials used, and how to assemble the glove, please watch the full length tutorial on my YouTube channel here:
.
Code
code
C/C++
#include <WiFi.h>
#include <ESP32Servo.h>
#include <esp_now.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
#include <FastLED.h>
#include <Wire.h>
// ----------------------------- SETTINGS -----------------------------
#define BUTTON_PIN 1
#define NEOPIXEL_PIN 10
#define NUM_PIXELS 8
#define I2C_SDA_PIN 5
#define I2C_SCL_PIN 6
#define LASER_SERVO_PIN 11
#define LASER_LED_PIN 2
// ----------------------------- LED BRIGHTNESS -----------------------
#define BRIGHTNESS_ANIMATED 200
#define BRIGHTNESS_STATIC 150
// ----------------------------- ESP-NOW MAC ADDRESSES ------------------------
uint8_t receiverAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t soundBoardMAC[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t leftRepulsorMAC[6] = { 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00 };
// ----------------------------- OBJECTS --------------------------------------
Adafruit_ADXL345_Unified accel(12345);
CRGB leds[NUM_PIXELS];
Servo laserServo;
// ----------------------------- LASER SERVOS ---------------------------------
int laserClosedPos = 180;
int laserOpenPos = 160;
int laserServoTarget = laserClosedPos;
int laserServoPos = laserClosedPos;
int laserOpenSpeed = 7;
int laserCloseSpeed = 7;
bool detachLaserServoOpen = false;
bool detachLaserServoClosed = true;
unsigned long laserDetachTime = 0;
unsigned long lastLaserUpdate = 0;
const unsigned long laserUpdateInterval = 20;
// ----------------------------- BUTTON VARIABLES -----------------------------
unsigned long buttonPressStartTime = 0;
const unsigned long longPressThreshold = 5000; // 5000ms for animated/static toggle
bool buttonDown = false;
unsigned long lastButtonUpTime = 0;
const unsigned long doubleClickDelay = 500;
int clickCount = 0;
// ----------------------------- REPULSOR STATE -----------------------------
enum FadeState { OFF, FADING_ON, ON, FADING_OFF, ALWAYS_ON, BLAST };
FadeState fadeState = OFF;
unsigned long fadeStartTime = 0;
const unsigned long fadeDuration = 500;
enum LedMode { ANIMATED, STATIC };
LedMode repulsorMode = ANIMATED;
const int filterSize = 20;
float angleBuffer[filterSize] = {0};
int bufferIndex = 0;
const unsigned long angleCheckInterval = 50;
unsigned long lastAngleCheckTime = 0;
unsigned long blastStartTime = 0;
const unsigned long blastDuration = 1500;
const unsigned long blastCooldown = 1000;
bool blastTriggered = false;
unsigned long lastBlastTime = 0;
float lastAccelMag = 0;
// ----------------------------- LASER FLICKER CONTROL -----------------------
unsigned long flickerDelayAfterOpen = 200;
unsigned long flickerTriggerTime = 0;
bool flickerScheduled = false;
bool flickering = false;
int flickerTotalCount = 4;
int flickerInterval = 30;
int remainingFlickers = 0;
int flickerState = 0;
unsigned long flickerStartTime = 0;
// ----------------------------- UTILITIES ------------------------------------
bool isValidMac(const uint8_t *addr) {
for (int i = 0; i < 6; ++i) if (addr[i] != 0x00) return true;
return false;
}
void safeAddPeer(const uint8_t *addr, const char *label) {
if (!isValidMac(addr)) return;
esp_now_peer_info_t p = {};
memcpy(p.peer_addr, addr, 6);
p.channel = 0;
p.encrypt = false;
esp_now_add_peer(&p);
}
void sendWirelessData(uint8_t data, const uint8_t *addr) {
if (!isValidMac(addr)) return;
esp_now_send(addr, &data, sizeof(data));
}
float calculateMovingAverage(float newValue) {
static float sum = 0;
static float readings[filterSize];
sum -= readings[bufferIndex];
readings[bufferIndex] = newValue;
sum += newValue;
bufferIndex = (bufferIndex + 1) % filterSize;
return sum / filterSize;
}
// ----------------------------- LASER CONTROL -------------------------------
void toggleLaser() {
// Reverse direction immediately, even mid-move
if (laserServoPos != laserServoTarget) {
laserServoTarget = (laserServoTarget == laserClosedPos) ? laserOpenPos : laserClosedPos;
} else {
laserServoTarget = (laserServoPos == laserClosedPos) ? laserOpenPos : laserClosedPos;
}
// Schedule flicker if opening
if (laserServoTarget == laserOpenPos) {
flickerScheduled = true;
flickerTriggerTime = millis() + flickerDelayAfterOpen;
digitalWrite(LASER_LED_PIN, LOW);
} else {
flickerScheduled = false;
digitalWrite(LASER_LED_PIN, LOW);
}
// Ensure servo is attached and clear detach
if (!laserServo.attached()) laserServo.attach(LASER_SERVO_PIN);
laserDetachTime = 0;
}
void updateLaserServo() {
unsigned long now = millis();
if (now - lastLaserUpdate < laserUpdateInterval) return;
lastLaserUpdate = now;
if (laserServoPos != laserServoTarget) {
if (!laserServo.attached()) laserServo.attach(LASER_SERVO_PIN);
int step = (laserServoTarget > laserServoPos) ? 1 : -1;
int speed = (laserServoTarget > laserServoPos) ? laserOpenSpeed : laserCloseSpeed;
int moveAmount = step * speed;
if (step > 0) moveAmount = min(moveAmount, laserServoTarget - laserServoPos);
else moveAmount = max(moveAmount, laserServoTarget - laserServoPos);
laserServoPos += moveAmount;
laserServo.write(laserServoPos);
laserDetachTime = 0;
if (laserServoPos == laserServoTarget) {
bool detachFlag = (laserServoPos == laserOpenPos) ? detachLaserServoOpen : detachLaserServoClosed;
if (detachFlag) laserDetachTime = now + 2000;
}
}
if (laserDetachTime && laserServoPos == laserServoTarget && now >= laserDetachTime) {
laserServo.detach();
laserDetachTime = 0;
}
}
// ----------------------------- BUTTON HANDLING ------------------------------
void handleButtonPress() {
int buttonState = digitalRead(BUTTON_PIN);
unsigned long now = millis();
if (buttonState == LOW && !buttonDown) {
buttonDown = true;
buttonPressStartTime = now;
}
if (buttonState == HIGH && buttonDown) {
buttonDown = false;
unsigned long pressDuration = now - buttonPressStartTime;
if (pressDuration >= longPressThreshold) {
if (repulsorMode == STATIC) {
repulsorMode = ANIMATED;
fadeState = OFF;
fadeStartTime = millis();
} else {
repulsorMode = STATIC;
fadeState = ALWAYS_ON;
}
} else {
clickCount++;
if (clickCount == 1) lastButtonUpTime = now;
}
}
if (clickCount == 1 && now - lastButtonUpTime > doubleClickDelay) {
sendWirelessData(101, receiverAddress);
clickCount = 0;
}
if (clickCount == 2) {
toggleLaser();
clickCount = 0;
}
}
// ----------------------------- REPULSOR – ANIMATED MODE -------------------
void handleAccelerometer() {
unsigned long now = millis();
if (now - lastAngleCheckTime < angleCheckInterval) return;
lastAngleCheckTime = now;
sensors_event_t evt;
accel.getEvent(&evt);
float angle = atan2(evt.acceleration.y, evt.acceleration.z) * 180 / PI;
float filtered = calculateMovingAverage(angle);
float accelMag = sqrt(evt.acceleration.x*evt.acceleration.x +
evt.acceleration.y*evt.acceleration.y +
evt.acceleration.z*evt.acceleration.z);
float accelMagChange = accelMag - lastAccelMag;
lastAccelMag = accelMag;
if (!blastTriggered && fadeState == ON && now - lastBlastTime > blastCooldown) {
if (accelMag > 9.0 && accelMagChange < -4.0) {
fadeState = BLAST;
blastTriggered = true;
blastStartTime = now;
lastBlastTime = now;
sendWirelessData(13, soundBoardMAC);
}
}
if (fadeState == BLAST && (now - blastStartTime > blastDuration)) {
blastTriggered = false;
fadeState = ON;
}
if (filtered <= -60 && fadeState == OFF) {
sendWirelessData(12, soundBoardMAC);
fadeState = FADING_ON;
fadeStartTime = now;
} else if (filtered >= -35 && fadeState == ON) {
sendWirelessData(11, soundBoardMAC);
fadeState = FADING_OFF;
fadeStartTime = now;
}
}
// ----------------------------- NEOPIXELS -------------------------------------
void handleNeoPixels() {
unsigned long now = millis();
CRGB baseColor = (repulsorMode == STATIC) ? CRGB(BRIGHTNESS_STATIC,BRIGHTNESS_STATIC,BRIGHTNESS_STATIC)
: CRGB(BRIGHTNESS_ANIMATED,BRIGHTNESS_ANIMATED,BRIGHTNESS_ANIMATED);
switch(fadeState) {
case OFF: fill_solid(leds, NUM_PIXELS, CRGB::Black); break;
case FADING_ON: {
int b = map(now - fadeStartTime, 0, fadeDuration, 0, 255);
CRGB fadedColor = baseColor;
fadedColor.nscale8_video(b);
fill_solid(leds, NUM_PIXELS, fadedColor);
if (now - fadeStartTime >= fadeDuration) fadeState = ON;
break;
}
case ON: fill_solid(leds, NUM_PIXELS, baseColor); break;
case FADING_OFF: {
int b = map(now - fadeStartTime, 0, fadeDuration, 255, 0);
CRGB fadedColor = baseColor;
fadedColor.nscale8_video(b);
fill_solid(leds, NUM_PIXELS, fadedColor);
if (now - fadeStartTime >= fadeDuration) fadeState = OFF;
break;
}
case ALWAYS_ON: fill_solid(leds, NUM_PIXELS, baseColor); break;
case BLAST:
for (int i=0;i<NUM_PIXELS;i++){
int flick = random(100,150);
leds[i] = CRGB(flick,flick,flick);
}
break;
}
FastLED.show();
}
// ----------------------------- LASER FLICKER EXECUTION ----------------------
void executeFlickerEffect() {
unsigned long now = millis();
if (now - flickerStartTime >= flickerInterval) {
flickerState = !flickerState;
digitalWrite(LASER_LED_PIN, flickerState);
if (flickerState == 0) {
remainingFlickers--;
if (remainingFlickers == 0) {
flickering = false;
digitalWrite(LASER_LED_PIN, HIGH);
}
}
flickerStartTime = now;
}
}
// ----------------------------- SETUP ----------------------------------------
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("=== Repulsor Glove + Laser Servo Boot ===");
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LASER_LED_PIN, OUTPUT);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) Serial.println("ESP-NOW init failed");
safeAddPeer(receiverAddress,"Receiver");
safeAddPeer(leftRepulsorMAC,"Left Glove");
safeAddPeer(soundBoardMAC,"Soundboard");
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
if(!accel.begin()) { Serial.println("ADXL345 not detected!"); while(1) delay(1000); }
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(leds, NUM_PIXELS);
FastLED.clear(); FastLED.show();
laserServoPos = laserClosedPos;
laserServoTarget = laserClosedPos;
laserServo.attach(LASER_SERVO_PIN);
laserServo.write(laserClosedPos);
delay(200);
laserServo.detach();
}
// ----------------------------- LOOP -----------------------------------------
void loop() {
handleButtonPress();
handleAccelerometer(); // ADDED: Repulsor triggers
handleNeoPixels();
updateLaserServo();
if (flickerScheduled && millis() >= flickerTriggerTime) {
flickerScheduled = false;
flickering = true;
remainingFlickers = flickerTotalCount;
flickerStartTime = millis();
}
if (flickering) executeFlickerEffect();
}
Nov 28,2025
147 views
Iron Man Repulsor Glove PCB (with laser)
This PCB will allow you to build your very own Iron Man repulsor glove with movement based lights and sound effects!
147
0
1
Published: Nov 28,2025
Standard PCB
Download Gerber file 8
Purchase
Donation Received ($)
PCBWay Donate 10% cost To Author
Only PCB
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.
Copy this HTML into your page to embed a link to order this shared project
Copy
Under the
Attribution-ShareAlike (CC BY-SA)
License.
Topic
- Comments(1)
- Likes(0)
Upload photo
You can only upload 5 files in total. Each file cannot exceed 2MB. Supports JPG, JPEG, GIF, PNG, BMP
0 / 10000
It looks like you have not written anything. Please add a comment and try again.
You can upload up to 5 images!
Image size should not exceed 2MB!
File format not supported!
View More
View More
VOTING
0 votes
- 0 USER VOTES
0.00
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Design
1/4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Usability
2/4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Creativity
3/4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Content
4/4
More by Plentiful_Props_3D
-
ESP32-S3 breakout board for motorizing Iron Man helmets
this PCB will allow you to create a breakcout board for a WaveShare ESP32-S3 specifically for the pu...
-
Iron Man Repulsor Glove PCB (with laser)
This PCB will allow you to build your very own Iron Man repulsor glove, complete with movement based...
-
Helldivers 2 Strataem Ball Prop PCB
This PCB will allow you to add lights and sounds to the MakerLab Stratagem Beacon from Helldivers 2!...
-
PP3D ESP32 breakout board for Iron Man helmet voice recognition module
this PCB is abreakout board for a WaveShare ESP32-S3 which will allow you to easily wire up and cont...
-
Iron Man Repulsor Glove PCB
This PCB will allow you to build your very own Iron Man repulsor glove, complete with movement based...
-
Helldivers 2 Sample Container Prop
Check out the drive link to get access to the other PCB gerber files (common sample and RGB array) a...
-
Iron Man Arc Reactor Prop
these PCBs combined with the modified crashworks code will allow you to make an illuminated Arc Reac...
-
Breakout Board for Motorizing Iron Man Cosplay Helmets!
This is a PCB breakout board intended to be used for motorizing Iron Man helmets. With an ATTiny85 U...
You may also like
-
-
AEL-2011 Power Supply Module
321 0 1 -
AEL-2011 50W Power Amplifier
295 0 1 -
-
-
Custom Mechanical Keyboard
564 0 0 -
Tester for Touch Screen Digitizer without using microcontroller
229 2 2 -
Audio reactive glow LED wristband/bracelet with NFC / RFID-Tags
235 0 1 -
-
-







