Kitty Ears: Understanding the Code

Hello,
I've been in the process of making cat ears for cosplay. Through research I found a simple tutorial for making such a thing here From this website the only deviation I made was changing the keyboard to individual tactile push buttons. I've got the code uploaded, and everything runs except it appears to be stuck on some kind of test loop where it cycles through various movements. I've been over the code trying to figure out where the issue is, or if it's actually an issue with the hardware, but I'm at a loss. I'm simply looking to have them do nothing unless a button is pressed.

I'm using a Nano, and followed the diagram the website provided. In the IDE I'm using for the board "Arduino Nano."

/*

*/

#include <Servo.h>


//Drives
#define       LeftVerPin                     3
#define       LeftAngPin                     5
#define       RightVerPin                    2
#define       RightAngPin                    4
//Buttons
#define       BUTTON_0_PIN                      6
#define       BUTTON_1_PIN                      7
#define       BUTTON_2_PIN                      8
#define       BUTTON_3_PIN                      9
#define       BUTTON_4_PIN                      10
#define       BUTTON_5_PIN                      11
#define       BUTTON_6_PIN                      12
#define       BUTTON_7_PIN                      13

#define       BUTTON_NUM                       8

#define       BUTTON_0                     0
#define       BUTTON_1                     1
#define       BUTTON_2                     2
#define       BUTTON_3                     3
#define       BUTTON_4                     4
#define       BUTTON_5                     5
#define       BUTTON_6                     6
#define       BUTTON_7                     7
#define       BUTTON_RELEASED              8

// Initial position of actuators
#define       INIT_EARS_POS                  90
#define       INIT_LEFT_VER_POS              130
#define       INIT_RIGHT_VER_POS             50
#define       INIT_LEFT_ANG_POS              80
#define       INIT_RIGHT_ANG_POS             100


Servo LeftVer;
Servo LeftAng;
Servo RightVer;
Servo RightAng;


void setup()
{
  byte BUTTON_PINS[BUTTON_NUM] = {BUTTON_0_PIN, BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN,
                              BUTTON_4_PIN, BUTTON_5_PIN, BUTTON_6_PIN, BUTTON_7_PIN};

  for(byte i = 0; i < BUTTON_NUM; i++)
  {
    
    pinMode(BUTTON_PINS[i], INPUT);
    digitalWrite(BUTTON_PINS[i], HIGH);
  }
  
  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);
  LeftAng.attach(LeftAngPin);
  RightAng.attach(RightAngPin);

  LeftVer.write(INIT_LEFT_VER_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  LeftAng.write(INIT_LEFT_ANG_POS);
  RightAng.write(INIT_RIGHT_ANG_POS);
  delay(300);

  LeftAng.detach();
  RightAng.detach();
  LeftVer.detach();
  RightVer.detach();
}
//------------------------------------------------------
void loop()
{
  byte Button = ScanButton();

  if (Button != BUTTON_RELEASED)
  {
    void (*EARS[BUTTON_NUM])(void) = {Ears_0, Ears_1, Ears_2, Ears_3,
                                    Ears_4, Ears_5, Ears_6, Ears_7};    
    (*EARS[Button])();
  }
}
//------------------------------------------------------
void Ears_0()
{
  const byte MaxAngleShift = 30;
  unsigned int MoveDelay = 5;
  unsigned int PosDelay = 500;

  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);
  
  for(byte i=0; i <= MaxAngleShift; i++)
  {
    LeftVer.write(INIT_LEFT_VER_POS - i);
    RightVer.write(INIT_RIGHT_VER_POS + i);
    delay(MoveDelay);
  }
  
  for(byte i=0; i <= 3*MaxAngleShift; i++)
  {
    LeftVer.write(INIT_LEFT_VER_POS - MaxAngleShift + i);
    RightVer.write(INIT_RIGHT_VER_POS + MaxAngleShift - i);
    delay(3*MoveDelay);
  }
  
  delay(PosDelay);

  LeftVer.write(INIT_LEFT_VER_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  delay(500);

  LeftVer.detach();
  RightVer.detach();
}
//------------------------------------------------------
void Ears_1()
{
  const byte MaxAngleShift = 100;
  unsigned int MoveDelay = 10;
  unsigned int PosDelay = 500;

  
  LeftAng.attach(LeftAngPin);
  RightAng.attach(RightAngPin);
  
  for(byte i=0; i <= MaxAngleShift; i++)
  {
    LeftAng.write(INIT_LEFT_ANG_POS + i);
    RightAng.write(INIT_RIGHT_ANG_POS - i);
    delay(MoveDelay);
  }
  LeftAng.detach();
  RightAng.detach();
  
  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);
  for(byte i=0; i <= MaxAngleShift/2; i++)
  {
    LeftVer.write(INIT_LEFT_VER_POS + i);
    RightVer.write(INIT_RIGHT_VER_POS - i);
    delay(MoveDelay/5);
  }
  
  delay(PosDelay);

  LeftAng.attach(LeftAngPin);
  RightAng.attach(RightAngPin);
  
  LeftVer.write(INIT_LEFT_VER_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  LeftAng.write(INIT_LEFT_ANG_POS);
  RightAng.write(INIT_RIGHT_ANG_POS);
  delay(500);

  LeftVer.detach();
  RightVer.detach();
  LeftAng.detach();
  RightAng.detach();
}
//------------------------------------------------------
void Ears_2()
{
  unsigned int MoveDelay = 10;
  byte AnglePos;
  
  LeftAng.attach(LeftAngPin);
  for(byte i=0; i <= 50; i++)
  {
    LeftAng.write(INIT_LEFT_ANG_POS + i);
    delay(3);
  }
  LeftAng.detach();

  AnglePos = INIT_LEFT_VER_POS;
  LeftVer.attach(LeftVerPin);
  for(byte i=0; i <= 50; i++)
  {
    LeftVer.write(AnglePos);
    AnglePos = INIT_LEFT_VER_POS + i;
    delay(MoveDelay);
  }
  
    
  for(byte i=0; i <= 10; i++)
  {
    LeftVer.write(AnglePos);
    AnglePos -= i;
    delay(MoveDelay);
  }
      
  for(byte j=0; j<5; j++)
  {
    for(byte i=0; i <= 10; i++)
    {
      LeftVer.write(AnglePos);
      AnglePos += i;
      delay(MoveDelay);
    }
    
    for(byte i=0; i <= 10; i++)
    {
      LeftVer.write(AnglePos);
      AnglePos -= i;
      delay(MoveDelay);
    }
  }

  
  LeftAng.attach(LeftAngPin);
  LeftVer.write(INIT_LEFT_VER_POS);
  LeftAng.write(INIT_LEFT_ANG_POS);
  delay(500);

  LeftAng.detach();
  LeftVer.detach();
}
//------------------------------------------------------
void Ears_3()
{
  unsigned int MoveDelay = 10;
  byte AnglePos;
  
  
  RightAng.attach(RightAngPin);
  for(byte i=0; i <= 50; i++)
  {
    RightAng.write(INIT_RIGHT_ANG_POS - i);
    delay(1);
  }
  RightAng.detach();

  AnglePos = INIT_RIGHT_VER_POS;
  RightVer.attach(RightVerPin);
  for(byte i=0; i <= 50; i++)
  {
    RightVer.write(AnglePos);
    AnglePos = INIT_RIGHT_VER_POS - i;
    delay(MoveDelay);
  }
  
  for(byte i=0; i <= 10; i++)
  {
    RightVer.write(AnglePos);
    AnglePos += i;
    delay(MoveDelay);
  }
    
  for(byte j=0; j<5; j++)
  {
    for(byte i=0; i <= 10; i++)
    {
      RightVer.write(AnglePos);
      AnglePos -= i;
      delay(MoveDelay);
    }
    
    for(byte i=0; i <= 10; i++)
    {
      RightVer.write(AnglePos);
      AnglePos += i;
      delay(MoveDelay);
    }
  }
  
  RightAng.attach(RightAngPin);
  RightVer.write(INIT_RIGHT_VER_POS);
  RightAng.write(INIT_RIGHT_ANG_POS);
  delay(500);

  RightAng.detach();
  RightVer.detach();
}
//------------------------------------------------------
void Ears_4()
{
  unsigned int MoveDelay = 10;
  byte AngleLeftPos;
  byte AngleRightPos;

  LeftAng.attach(LeftAngPin);
  RightAng.attach(RightAngPin);
  for(byte i=0; i <= 50; i++)
  {
    LeftAng.write(INIT_LEFT_ANG_POS + i);
    RightAng.write(INIT_RIGHT_ANG_POS - i);
    delay(3);
  }
  LeftAng.detach();
  RightAng.detach();

  AngleLeftPos = INIT_LEFT_VER_POS;
  AngleRightPos = INIT_RIGHT_VER_POS;
  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);
  for(byte i=0; i <= 50; i++)
  {
    LeftVer.write(AngleLeftPos);
    RightVer.write(AngleRightPos);
    AngleLeftPos = INIT_LEFT_VER_POS + i;
    AngleRightPos = INIT_RIGHT_VER_POS - i;
    delay(MoveDelay);
  }

  for(byte i=0; i <= 10; i++)
  {
    LeftVer.write(AngleLeftPos);
    RightVer.write(AngleRightPos);
    AngleLeftPos -= i;
    AngleRightPos += i;
    delay(MoveDelay);
  }

  for(byte j=0; j<5; j++)
  {
    for(byte i=0; i <= 10; i++)
    {
      LeftVer.write(AngleLeftPos);
      RightVer.write(AngleRightPos);
      AngleLeftPos += i;
      AngleRightPos -= i;
      delay(MoveDelay);
    }
    
    for(byte i=0; i <= 10; i++)
    {
      LeftVer.write(AngleLeftPos);
      RightVer.write(AngleRightPos);
      AngleLeftPos -= i;
      AngleRightPos += i;
      delay(MoveDelay);
    }
  }

  LeftAng.attach(LeftAngPin);
  RightAng.attach(RightAngPin);
  LeftVer.write(INIT_LEFT_VER_POS);
  LeftAng.write(INIT_LEFT_ANG_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  RightAng.write(INIT_RIGHT_ANG_POS);
  delay(300);

  LeftAng.detach();
  LeftVer.detach();
  RightAng.detach();
  RightVer.detach();
}
//------------------------------------------------------
void Ears_5()
{
  const byte MaxAngleShift = 50;
  unsigned int PosDelay = 300;

  byte PosLeft = INIT_LEFT_VER_POS;
  byte PosRight = INIT_RIGHT_VER_POS;
  
  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);

  PosLeft -= MaxAngleShift;
  PosRight -= MaxAngleShift;
  LeftVer.write(PosLeft);
  RightVer.write(PosRight);

  delay(PosDelay);

  for(byte i = 0; i < 2; i++)
  {
    PosLeft += 2*MaxAngleShift;
    PosRight += 2*MaxAngleShift;
    LeftVer.write(PosLeft);
    RightVer.write(PosRight);
    delay(PosDelay);
    PosLeft -= 2*MaxAngleShift;
    PosRight -= 2*MaxAngleShift;
    LeftVer.write(PosLeft);
    RightVer.write(PosRight);
    delay(PosDelay);
  }

  LeftVer.write(INIT_LEFT_VER_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  delay(500);

  LeftVer.detach();
  RightVer.detach();
}
//------------------------------------------------------
void Ears_6()
{
  const byte MaxAngleShift = 70;
  
  LeftVer.attach(LeftVerPin);
  RightVer.attach(RightVerPin);
  LeftVer.write(INIT_LEFT_VER_POS - MaxAngleShift);
  RightVer.write(INIT_RIGHT_VER_POS + MaxAngleShift);
  delay(1000);
    
  for(byte i=0; i <= MaxAngleShift; i++)
  {
    LeftVer.write(INIT_LEFT_VER_POS - MaxAngleShift + i);
    RightVer.write(INIT_RIGHT_VER_POS + MaxAngleShift - i);
    delay(30);
  }
  delay(500);

  LeftVer.write(INIT_LEFT_VER_POS);
  RightVer.write(INIT_RIGHT_VER_POS);
  delay(500);

  LeftVer.detach();
  RightVer.detach();
}
//------------------------------------------------------
void Ears_7()
{
  const unsigned int pause = 3000;
  
  Ears_0();
  delay(pause);
  Ears_1();
  delay(pause);
  Ears_2();
  delay(pause);
  Ears_3();
  delay(pause);
  Ears_4();
  delay(pause);
  Ears_5();
  delay(pause);
  Ears_6();
}
//-----------------------------------
//Button test
byte ScanButton()
{
  byte BUTTON_PINS[BUTTON_NUM] = {BUTTON_0_PIN, BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN,
                              BUTTON_4_PIN, BUTTON_5_PIN, BUTTON_6_PIN, BUTTON_7_PIN};

  for (byte i = 0; i < BUTTON_NUM; i++)
  {
    if (digitalRead(BUTTON_PINS[i]) == 0)
    {
      delay(70);
      if (digitalRead(BUTTON_PINS[i]) == 0)
        return i;
    }
  }
  
  return BUTTON_RELEASED;
}

Diagram I followed:

I am new to Arduino, my only experience with coding is Python. I've ran through various projects and help forums on here, but I have not found anything that had a similar situation. I would be extremely grateful for any help!

Thank you,
Amanda

It would help to see a picture of your wiring too.

The 5V output of the Nano cannot be used to power servos, not even one. Use a separate servo power supply, and connect the grounds. A 4XAA battery pack can power one or two small servos.

first things first…
Lose all those attach and detach calls in loop().

byte ScanButton()
{
  byte BUTTON_PINS[BUTTON_NUM] = {BUTTON_0_PIN, BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN,
                              BUTTON_4_PIN, BUTTON_5_PIN, BUTTON_6_PIN, BUTTON_7_PIN};

  for (byte i = 0; i < BUTTON_NUM; i++)
  {
    if (digitalRead(BUTTON_PINS[i]) == 0)
    {
      delay(70);
      if (digitalRead(BUTTON_PINS[i]) == 0)
        return i;
    }
  }
  
  return BUTTON_RELEASED;
}

I think you are constantly running Ears7() because the button connected to pin13 is reading LOW.

Pin 13 is connected to the onboard led and on the Nano, even with the internal pullup, it will read low as an input.

For a simple test, ground both sides of that button and see if the problem is fixed. If using pin 13 as an input is the root cause, you will need to switch it to another pin, or don't use it and live without the Ears7 test program.

Thank you for responding. I'm not sure I understand what you mean, loop() doesn't (as far as I can tell) have any attach or detach calls. Could you elaborate?

loop() calls functions that call attach() and detach()

Thank you for responding. I'm okay with not having the test program, and tried to do away with that section by replacing it with Ear6() (I didn't know how else to get rid of it, when I tried to delete it I got an error when verifying the code), but I started looping through Ear6(). I've been reading through different commands and variables to see if I can figure it out, but I can't see where it would constantly loop without pressing a button. Any ideas?

I tried getting rid of all the attach() and detach() parts, but nothing changed. Also could you tell me which line calls attach() and detach()? is it:

void (*EARS[BUTTON_NUM])(void) = {Ears_0, Ears_1, Ears_2, Ears_3,
                                    Ears_4, Ears_5, Ears_6, Ears_7};

Hi again @cattledog I removed BUTTON_6_PIN from the options, and the good news is it is no longer running through the test! However, it does loop through whatever option when I hold a button down, is this because of the delay(70), or something else? I just want it to move and hold until I release the button.

These are small servos, and to run the program I'm not actually using the battery supply. Would an Uno be a better option?

The wiring is the same as in the attached image.

@UKHeliBob reesponded before Insaw your reply…

The attaches inside setup() are good.

I probably should have been clearer, all the attaches & detaches called while you’re running in loop(). i.e. all the sub-functions that keep connecting to the servos, moving them then disconnecting… are unnecessary.

ALL the detaches are not needed until you’re putting the program to sleep or shutting down.

You don’t need to attach & detach every time you want to use the servos, that’s probably why they twitch and do strange things each time you call one of those functions.

Okay, I think I understand. Thanks! I'll give it a shot and see if I've got it right.

Almost never! :astonished:

You need to supply 5 V to the servos. Supplying 5 V to the servos has absolutely nothing to do with the Arduino, the only thing that is required is to connect the Arduino ground to the ground of all the servos. The 5 V supply avoids going through the Arduino but you can and indeed should connect it to the "5V" pin in order to power the Nano from that 5 V supply when not connected to the PC.

If however you use a 6 V battery (four alkaline "AA" cells in a carrier) to power the servos, you can just use a silicon power diode in series to drop the voltage by about 0.6 V to feed the "5V" pin on the Nano.

You will not be able to make this project work until the servos and the Arduino are correctly and separately powered.

The servo power supply must be capable of supplying 4.8 to 6V at 1 Ampere per servo (for small servos like SG-90). The Arduino power supply should be completely independent of the servos. Connect the grounds.

Yes, the pattern will repeat when the button is held down. I'm not clear how long any pattern takes, but if you release the button while it is running it should not run again.

If you want the pattern to run only once, even if you keep holding the button down you will have to reconfigure the button reading code to respond when the button has become pressed instead of when it is pressed. See the state change example of the ide.

You will have something like
if (digitalRead(BUTTON_PINS[i]) == 0 and lastdigitalRead(BUTTON_PINS[i]) ==1)

1 Like

Thank you so much for your help, I think this will get me on the right path.