touchmysound
Published © CC BY

๐ŸŽธ Hacking Auto-Tuning Guitar Pegs for Arduino/MIDI Control

I wanted motorized glissandos on electric guitar stings, so the best solution was to hack already available self-tuning systems ๐Ÿ˜›

AdvancedFull instructions provided12 hours5,795

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Stepper motor driver board A4988
SparkFun Stepper motor driver board A4988
I actually used a Sparkfun Quadstepper board since it was cheaper. You may still find it but it's discontinued.
×3
AC/DC Power Supply, 12 V
AC/DC Power Supply, 12 V
×1
Tronicaltune Motorized pegs
Choose wisely depending on your electric guitar
×1
Fender Squier electric guitar
Or other model :)
×1

Software apps and online services

MAX
Cycling '74 MAX
Reaper
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter

Story

Read more

Custom parts and enclosures

Holder for hacked Tronicaltune motorized pegs, to be mounted on a classical guitar

The Tronicaltune system is a set of motorized tuning pegs for electric guitar that are meant to automatically tune the guitar at your command.
I have hacked the tuning pegs so that they can be controlled via MIDI. With this mount you can attach them to your classical guitar.

Schematics

Schematics of the connections

Connects the Arduino to the stepper drivers and to the motorized pegs.

Code

Arduino code for controlling three stepper motors, via serial.

Arduino
Part of a project where I controlled three motorized tuning pegs (electric guitar) via MIDI.
//Controlling tronicaltune motorized pegs (or actually any stepper motor) via MIDI (DAW + Cycling '74's Max)
// www.alessandroperini.com

// DIR = HIGH ----> HEADING RIGHT
// DIR = LOW  ----> HEADING LEFT

const byte numChars = 72;
char receivedChars[numChars];
byte m1on;
byte m1sp;
byte m2on;
byte m2sp;
byte m3on;
byte m3sp;
byte globalSpeed;
boolean newData = false;

const int step1 = 3;
const int dir1 = 6;

const int step2 = 4;
const int dir2 = 7;

const int step3 = 5;
const int dir3 = 8;

const int enablePin = 2; //enable (MUST BE LO TO OPERATE; SEE BOTTOM NOTES)
const int slp1 = 9; // Sleep  (MUST BE HI TO OPERATE; SEE BOTTOM NOTES)
const int slp2 = 10; // Sleep  (MUST BE HI TO OPERATE; SEE BOTTOM NOTES)
const int slp3 = 11; // Sleep  (MUST BE HI TO OPERATE; SEE BOTTOM NOTES)
const int micro1Pin = 12; // Microstep 1

const int lowestMd = 3; // minimum microdelay



void setup() {


  pinMode(step1, OUTPUT);
  pinMode(dir1, OUTPUT);
  pinMode(step2, OUTPUT);
  pinMode(dir2, OUTPUT);
  pinMode(step3, OUTPUT);
  pinMode(dir3, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(slp1, OUTPUT);
  pinMode(slp2, OUTPUT);
  pinMode(slp3, OUTPUT);
  pinMode(micro1Pin, OUTPUT);

  digitalWrite(step1, LOW);
  digitalWrite(step2, LOW);
  digitalWrite(step3, LOW);

  digitalWrite(enablePin, LOW);
  digitalWrite(micro1Pin, LOW);
  digitalWrite(slp1, LOW);
  digitalWrite(slp2, LOW);
  digitalWrite(slp3, LOW);

  Serial.begin(38400);

}
void loop() {
  recvWithStartEndMarkers();
  writeToMotors();
  showNewData();

}


////////////////////////////////////////////////////////////////////////////////////////////


void recvWithStartEndMarkers() {        // RECEIVE THE BYTES, USING START AND END MARKERS
  static boolean recvInProgress = false;
  static byte ndx = 0;
  byte startMarker = 255;
  byte endMarker = 250;
  byte rc;

  //
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void showNewData() {
  if (newData == true) {
    m1on = (byte)receivedChars[0];
    m1sp = (byte)receivedChars[1];
    m2on = (byte)receivedChars[2];
    m2sp = (byte)receivedChars[3];
    m3on = (byte)receivedChars[4];
    m3sp = (byte)receivedChars[5];
    globalSpeed = (byte)receivedChars[6];

    Serial.print(" m1on ");
    Serial.print(m1on);
    Serial.print("\t");
    Serial.print(" m1sp ");
    Serial.print(m1sp);
    Serial.print("\t");
    Serial.print(" m2on ");
    Serial.print(m2on);
    Serial.print("\t");
    Serial.print(" m2sp ");
    Serial.print(m2sp);
    Serial.print("\t");
    Serial.print(" m3on ");
    Serial.print(m3on);
    Serial.print("\t");
    Serial.print(" m3sp ");
    Serial.print(m3sp);
    Serial.print("\t");
    Serial.print(" globalSpeed ");
    Serial.println(globalSpeed);

    newData = false;
  }
}


void writeToMotors() {

  /// CHECK IF STEPPING ///

  if (m1on == 0) {
    digitalWrite(step1, LOW);
   digitalWrite(slp1, LOW);
  }  else  {
    digitalWrite(step1, HIGH);
   digitalWrite(slp1, HIGH);
  }
  if (m2on == 0) {
    digitalWrite(step2, LOW);
    digitalWrite(slp2, LOW);
  }  else  {
    digitalWrite(step2, HIGH);
    digitalWrite(slp2, HIGH);
  }
  if (m3on == 0) {
    digitalWrite(step3, LOW);
    digitalWrite(slp3, LOW);
  }  else  {
    digitalWrite(step3, HIGH);
    digitalWrite(slp3, HIGH);
  }


  /// DIR ///

  if (m1sp >= 64) {
    digitalWrite(dir1, HIGH);
    
  }
  else {
    digitalWrite(dir1, LOW);
  }

  if (m2sp >= 64) {
    digitalWrite(dir2, HIGH);
  }
  else {
    digitalWrite(dir2, LOW);
  }

  if (m3sp >= 64) {
    digitalWrite(dir3, HIGH);
  }
  else {
    digitalWrite(dir3, LOW);
  }


  int scaledSpeed = (lowestMd + 127 * 25) - globalSpeed * 25;
  delayMicroseconds(scaledSpeed);

  digitalWrite(step1, LOW);
  digitalWrite(step2, LOW);
  digitalWrite(step3, LOW);

  delayMicroseconds(scaledSpeed);
}



/*https://www.pololu.com/file/0J450/A4988.pdf

   Sleep Mode (SLEEP ). To minimize power consumption
  when the motor is not in use, this input disables much of the
  internal circuitry including the output FETs, current regulator,
  and charge pump. A logic low on the SLEEP pin puts the A4988
  into Sleep mode. A logic high allows normal operation, as well as
  start-up (at which time the A4988 drives the motor to the Home
  microstep position). When emerging from Sleep mode, in order
  to allow the charge pump to stabil



  Enable Input. This input turns on or off all of the
  FET outputs. When set to a logic high, the outputs are disabled.
  When set to a logic low, the internal control enables the outputs
  as required. The translator inputs STEP, DIR, and MSx, as well as
  the internal sequencing logic, all remain active, independent of the
  ENABLE input state.



  Reset Input. Sets the translator
  to a predefined Home state (shown in Figures 9 through 13), and
  turns off all of the FET outputs. All STEP inputs are ignored until
  the Reset input is set to high.


  https://www.pololu.com/product/1182
  Each pulse to the STEP input corresponds to one microstep of the stepper motor in the direction selected by the DIR pin. Note that the STEP and DIR pins are not pulled to any particular voltage internally, so you should not leave either of these pins floating in your application. If you just want rotation in a single direction, you can tie DIR directly to VCC or GND. The chip has three different inputs for controlling its many power states: RST, SLP, and EN. For details about these power states, see the datasheet. Please note that the RST pin is floating; if you are not using the pin, you can connect it to the adjacent SLP pin on the PCB to bring it high and enable the board.


*/

Credits

touchmysound

touchmysound

6 projects โ€ข 44 followers
Alessandro Perini's artistic production ranges from audiovisual and light-based works to net-art, land-art and vibration-based works.
Thanks to Johan Nordstrรถm and Ruben Mattia Santorsa.

Comments