Hello everyone,
This is my first time posting so please forgive me for any formatting mistakes. If this topic has already been addressed somewhere I have yet to find, please point me in the right direction.
I've been working on a project off and on for over a year now, constantly running into problems, scouring the webs for an answer, and then running into the next problem. So here I am screaming into the void, hoping the void whispers back.
I'm working on a cosplay Gray Fox helmet. It uses 4 servos, and many LEDs to open the mask and make the eyes and other bits blink/ light up when starting up.
Having no real experience coding outside of many youtube vids, I was able to find the code below for an iron man helmet which I was able to successfully modify and mock up to work on an Arduino Uno.
However, now I want to try to make it better by
1; using a smaller microcontroller. I'm looking at the Seeed Studio Xiao-ESP32-C6 because I believe that it is small enough to fit and has at least 6 PWM pins that I can use.
2; I want to use a NeoPixel Ring for the Mono eye and the single WS2812 LEDs for the rest of the helmet.
The code below has a 'blink on startup command' that runs on a button push which I'm having trouble getting to play nice with the Fast LED or Neopixel Libraries that I have tried to make work.
I appreciate any and all help I can get... I need it.
This is the code that I'm using.
//Gray Fox Helmet Code 2.0
#include <VarSpeedServo.h> // servo reference library, for instructions see: https://github.com/netlabtoolkit/VarSpeedServo
#include <Bounce2.h>
#include <ButtonEvents.h> // button effects, for instructions see: https://github.com/fasteddy516/ButtonEvents
const int servo1Pin = 6; // set the pin for servo 1 (Right Cheek)
const int servo2Pin = 9; // set the pin for servo 2 (Right Top Visor)
const int servo3Pin = 10; // set the pin for servo 3 (Left Top Visor)
const int servo4Pin = 11; // set the pin for servo 4 (Left Cheek)
const int buttonPin = 2; // the pin that the pushbutton is attached to
const int monoEyePin = 3; // set the pin for the mono eye (this will need to me modified for the led ring with multiple leds)
const int helmetLEDsPin = 5 // set the pin for the LEDs around the helmet
// Declare servo objects
;VarSpeedServo servo1; // create servo object to control servo 1 (Right Cheek)
;VarSpeedServo servo2; // create servo object to control servo 2 (Right Top Visor)
;VarSpeedServo servo3; // create servo object to control servo 3 (Left Top Visor)
;VarSpeedServo servo4; // create servo objest to control servo 4 (Left Cheek)
// Declare variables for servo speed control
const int servoCloseSpeed = 255; // set the speed of the servo close function
const int servoOpenSpeed = 255; // set the speed of the servo opening, recommend to set at max speed to aid in lift
// In Dual Servo Configuration the servos move in opposing directions, so the angles of the servos will be opposite to each other.
// Normal Servo range is 0° ~ 180°, for initial setup the range has been adjusted to 20° ~ 160°, this allows for a 20° adjustment at both ends of the servo range.
// See Helmet tutorial for further information on servo setup.
const int servo1_OpenPos = 20; // set the open position of servo 1 (Right Cheek)
const int servo2_OpenPos = 110; // set the open position of servo 2 (Right Top Visor)
const int servo3_OpenPos = 20; // set the open position of servo 3 (Left Top Visor)
const int servo4_OpenPos = 110; // set the open position of servo 4 (Left Cheek)
const int servo1_ClosePos = 110; // set the closed position of servo 1 (Right Cheek)
const int servo2_ClosePos = 20; // set the closed position of servo 2 (Right Top Visor)
const int servo3_ClosePos = 110; // set the closed position of servo 3 (Left Top Visor)
const int servo4_ClosePos = 20; // set the closed position of servo 4 (Left Cheek)
// Declare variables for setup special effects (applies to LED eyes only for now)
#define SETUP_NONE 0 // No special effects, just turn on the LED eyes
#define SETUP_MOVIE_BLINK 1 // Blink LED eyes on setup,
#define SETUP_FADE_ON 2 // Slowly brighten LED eyes until fully lit
// To use the specific feature below
// use double slashes "//" to comment, or uncomment (remove double slashes) in the code below
// Uncomment this line if you don't want any special effect during setup, comment this line to disable this effect
// const int setupFx = SETUP_NONE;
// Uncomment this line if you want the movie blink special effect during setup, comment this line to disable this effect
const int setupFx = SETUP_MOVIE_BLINK;
// Uncomment this line if you want the fade on special effect during setup, comment this line to disable this effect
// const int setupFx = SETUP_FADE_ON;
// Declare variables for LED eyes special effects (applies to LED eyes only for now)
#define EYES_NONE 0 // No special effects, just turn on the LED eyes
#define EYES_MOVIE_BLINK 1 // Blink LED eyes on setup, sequence based on Avengers Movie
#define EYES_FADE_ON 2 // Slowly brighten LED eyes until fully lit
// To use the specific feature below
// use double slashes "//" to comment, or uncomment (remove double slashes) in the code below
// Uncomment this line if you don't want any special effect during setup, comment this line to disable this effect
// const int eyesFx = EYES_NONE;
// Uncomment this line if you want the movie blink special effect during setup, comment this line to disable this effect
const int eyesFx = EYES_MOVIE_BLINK;
// Uncomment this line if you want the fade on special effect during setup, comment this line to disable this effect
//const int eyesFx = EYES_FADE_ON;
// Declare variables for button control
boolean movieblinkOnClose = false; //Blink LEDs on close of faceplate,
// Declare variable for AuxLED
//boolean auxLedEnabled = false; // Set to true if you want to enable the Aux LED
//boolean auxLedState = true; // Keeps track of the state of the LED on = true, off = false
// Declare variables for LED control
unsigned long fadeDelay = .1; //speed of the eye 'fade'
unsigned long callDelay = 10; //length to wait to start eye flicker after face plate comes down
unsigned long blinkSpeed = 60; //delay between init blink on/off
unsigned long currentPWM = 0; // keep track of where the current PWM level is at
boolean isOpen = true; // keep track of whether or not the faceplate is open
// Define object for primary button to handle
// multiple button press features:
// 1. Single Tap
// 2. Double Tap
// 3. Long Press
ButtonEvents primaryButton = ButtonEvents();
// State of the faceplate 1 = open, 0 = closed
#define FACEPLATE_CLOSED 0
#define FACEPLATE_OPEN 1
int facePlateCurMode = FACEPLATE_OPEN; // Keep track if the faceplate is open or closed
// State of the LED eyes 1 = on, 2 = off
#define LED_EYES_OFF 0
#define LED_EYES_ON 1
// State of the LED eyes for dimming/brightening 1 = brighten, 2 = dim
#define LED_EYES_DIM_MODE 0
#define LED_EYES_BRIGHTEN_MODE 1
int ledEyesCurMode = LED_EYES_DIM_MODE; // Keep track if we're dimming or brightening
int ledEyesCurPwm = 0; // Tracking the level of the LED eyes for dim/brighten feature
const int ledEyesIncrement = 15; // Define the increments to brighten or dim the LED eyes
/**
* Helper Method
* Simulate a delay in processing without disabling the processor completely
*
* @param[out] period - the amount of time in milliseconds to delay
*
* See: https://randomnerdtutorials.com/why-you-shouldnt-always-use-the-arduino-delay-function/
*/
void simDelay(long period){
long delayMillis = millis() + period;
while (millis() <= delayMillis)
{
int x = 0; // dummy variable, does nothing
}
}
/**
* Simulate the eyes slowly blinking until fully lit
*/
void movieblink(){
Serial.println(F("Start Movie Blink.."));
// pause for effect...
simDelay(300);
int lowValue = 21;
int delayInterval[] = { 210, 126, 84 };
int delayVal = 0;
// First blink on
for (int i = 0; i <= lowValue; i++){
setLedEyes(i);
delayVal = delayInterval[0]/lowValue;
simDelay(delayVal);
}
// Turn off
setLedEyes(0);
simDelay(delayInterval[0]);
// Second blink on
for (int i = 0; i <= lowValue; i++){
setLedEyes(i);
//setAuxLed();
delayVal = delayInterval[1]/lowValue;
simDelay(delayVal);
}
// Turn off
setLedEyes(0);
//setAuxLed();
simDelay(delayInterval[1]);
// Third blink on
setLedEyes(lowValue);
//setAuxLed();
simDelay(delayInterval[2]);
// Turn off
setLedEyes(0);
//setAuxLed();
simDelay(delayInterval[2]);
// All on
setLedEyes(255);
}
/*
* Simulate LED eyes slowly brightening until fully lit
*/
void fadeEyesOn(){
ledEyesCurMode = LED_EYES_BRIGHTEN_MODE;
// loop until fully lit
while (ledEyesCurPwm < 255){
setLedEyes(ledEyesCurPwm);
simDelay(200);
ledEyesBrighten();
}
}
/**
* Method to open face plate
*/
void facePlateOpen(){
Serial.println(F("Servo Up!"));
// Re-attach the servos to their pins
servo1.attach(servo1Pin);
servo2.attach(servo2Pin);
servo3.attach(servo3Pin);
servo4.attach(servo4Pin);
// Send data to the servos for movement
servo1.write(servo1_OpenPos, servoOpenSpeed);
servo4.write(servo4_OpenPos, servoOpenSpeed);
//simDelay(1000); // wait doesn't wait long enough for servos to fully complete...
simDelay(500);
servo2.write(servo2_OpenPos, servoOpenSpeed);
servo3.write(servo3_OpenPos, servoOpenSpeed);
simDelay(1000); // wait doesn't wait long enough for servos to fully complete...
// Detach so motors don't "idle"
servo1.detach();
servo2.detach();
servo3.detach();
servo4.detach();
facePlateCurMode = FACEPLATE_OPEN;
}
/**
* Method to close face plate
*/
void facePlateClose(){
Serial.println(F("Servo Down"));
// Re-attach the servos to their pins
servo1.attach(servo1Pin);
servo2.attach(servo2Pin);
servo3.attach(servo3Pin);
servo4.attach(servo4Pin);
// Send data to the servos for movement
servo2.write(servo2_ClosePos, servoCloseSpeed);
servo3.write(servo3_ClosePos, servoCloseSpeed);
simDelay(500);
servo1.write(servo1_ClosePos, servoCloseSpeed);
servo4.write(servo4_ClosePos, servoCloseSpeed);
// Delay to allow Jaw to fully close before Faceplate closes
simDelay(1000); // wait doesn't wait long enough for servos to fully complete...
// Detach so motors don't "idle"
servo1.detach();
servo2.detach();
servo3.detach();
servo4.detach();
facePlateCurMode = FACEPLATE_CLOSED;
}
/**
* Set the brightness of the LED eyes
*
* @param[out] pwmValue - the PWM value (0-255) for the LED brightness
*/
void setLedEyes(int pwmValue){
analogWrite(monoEyePin, pwmValue);
analogWrite(helmetLEDsPin, pwmValue);
ledEyesCurPwm = pwmValue;
}
/**
* Method to turn on LED eyes
*/
void ledEyesOn(){
Serial.println(F("Turning LED eyes on..."));
setLedEyes(255);
ledEyesCurMode = LED_EYES_DIM_MODE;
}
/**
* Method to turn off LED eyes
*/
void ledEyesOff(){
Serial.println(F("Turning LED eyes off..."));
setLedEyes(0);
ledEyesCurMode = LED_EYES_BRIGHTEN_MODE;
}
/**
* Method to turn LED eyes on/off
*/
void ledEyesOnOff(){
// LED eyes stay off when faceplate is open
if(facePlateCurMode == FACEPLATE_CLOSED){
if (ledEyesCurPwm > 0){
ledEyesOff();
} else {
ledEyesOn();
}
}
}
void ledEyesDim(){
Serial.println(F("Dimming LED eyes..."));
ledEyesCurPwm = ledEyesCurPwm - ledEyesIncrement; // Decrease the brightness
// Make sure we don't go over the limit
if(ledEyesCurPwm <= 0){
ledEyesCurPwm = 0;
}
}
void ledEyesBrighten(){
Serial.println(F("Brightening LED eyes..."));
ledEyesCurPwm = ledEyesCurPwm + ledEyesIncrement; // Increase the brightness
// Make sure we don't go over the limit
if(ledEyesCurPwm >= 255){
ledEyesCurPwm = 255;
}
}
/**
* Method to dim or brighten both LED eyes
*/
void ledEyesFade(){
if(ledEyesCurPwm == 255){
ledEyesCurMode = LED_EYES_DIM_MODE;
} else if(ledEyesCurPwm == 0){
ledEyesCurMode = LED_EYES_BRIGHTEN_MODE;
}
if(ledEyesCurMode == LED_EYES_BRIGHTEN_MODE){
ledEyesBrighten();
} else {
ledEyesDim();
}
setLedEyes(ledEyesCurPwm);
simDelay(200);
}
/**
* Method to run sequence of sppecial effects when system first starts or sets up
*/
void startupFx(){
//facePlateClose();
facePlateClose();
switch(setupFx){
case SETUP_NONE:
ledEyesOn();
break;
case SETUP_MOVIE_BLINK:
movieblink();
break;
case SETUP_FADE_ON:
fadeEyesOn();
break;
}
}
/**
* Method to execute special effects when the faceplate opens
*/
void facePlateOpenFx(){
// TODO: See if we need delays in between fx
ledEyesOff();
facePlateOpen();
}
/**
* Method to execute special effects when the faceplate closes
*/
void facePlateCloseFx(){
facePlateClose();
switch(eyesFx){
case EYES_NONE:
ledEyesOn();
break;
case EYES_MOVIE_BLINK:
movieblink();
break;
case EYES_FADE_ON:
fadeEyesOn();
break;
}
}
/**
* Handle faceplate special effects
*/
void facePlateFx(){
if (facePlateCurMode == FACEPLATE_OPEN){
facePlateCloseFx();
} else {
facePlateOpenFx();
}
}
/**
* Event handler for when the primary button is tapped once
*/
void handlePrimaryButtonSingleTap(){
facePlateFx();
}
/**
* Event handler for when the primary button is double tapped
*/
void handlePrimaryButtonDoubleTap(){
ledEyesOnOff();
}
/**
* Event handler for when the primary button is pressed and held
*/
void handlePrimaryButtonLongPress(){
while(!primaryButton.update()){
ledEyesFade(); // Dim or brighten the LED eyes
}
}
/**
* Initializes the primary button for multi-functions
*/
void initPrimaryButton(){
// Attach the button to the pin on the board
primaryButton.attach(buttonPin, INPUT_PULLUP);
// Initialize button features...
primaryButton.activeLow();
primaryButton.debounceTime(15);
primaryButton.doubleTapTime(250);
primaryButton.holdTime(2000);
}
/**
* Monitor for when the primary button is pushed
*/
void monitorPrimaryButton(){
bool changed = primaryButton.update();
// Was the button pushed?
if (changed){
int event = primaryButton.event(); // Get how the button was pushed
switch(event){
case(tap):
Serial.println(F("Primary button single press..."));
handlePrimaryButtonSingleTap();
break;
case (doubleTap):
Serial.println(F("Primary button double press..."));
handlePrimaryButtonDoubleTap();
break;
case (hold):
Serial.println(F("Primary button long press..."));
handlePrimaryButtonLongPress();
break;
}
}
}
/**
* Initialization method called by the Arduino library when the board boots up
*/
void setup() {
// Set up serial port
Serial.begin(115200);
simDelay(2000); // Give the serial service time to initialize
Serial.print(F("Initializing Iron Man Servo version: "));
//Serial.println(VERSION);
#ifdef SOUND
init_player(); // initializes the sound player
#endif
initPrimaryButton(); // initialize the primary button
//pinMode(AuxLED, OUTPUT); // set output for AUX LED
startupFx(); // Run the initial features
}
/**
* Main program exeucution
* This method will run perpetually on the board
*/
void loop() {
monitorPrimaryButton(); // Since all features currently are tied to the one button...
}