Project tutorial

🎸 Hacking Auto-Tuning Guitar Pegs for Arduino/MIDI Control © CC BY

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

  • 4,026 views
  • 0 comments
  • 6 respects

Components and supplies

Ph a000066 iso (1) ztbmubhmho
Arduino UNO
×1
11026 02
Jumper wires (generic)
×1
12859 01
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
95w2620 40
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

Necessary tools and machines

Apps and online services

About this project

I've always wanted to automatically detune and retune the strings of a guitar while the guitarist was playing. In this project I tried, and managed, to do exactly that πŸ˜› with the help of technician Johan NΓΆrdstrom. After completing the project I wrote some music for guitarist Ruben Mattia Santorsa.

Check the track on Spotify.

"RondΓ²" for electric guitar with three MIDI-controlled tuning pegs by Alessandro Perini, played by Ruben Mattia Santorsa

Also, check this video with Bertrand Chavarria-Aldrete improvising while the motors are modifying the tuning:

https://www.instagram.com/tv/CFRdw-bg9xS/?utm_source=ig_web_copy_link

Attempt 1

The first attempt was on a classical guitar. Johan and me tried to build a system with two linear motors that would move a second, mobile bridge up and down, in fact modifying the length of the strings. It didn't work as planned, since the noise of the motors was overwhelming, killing the feeble sound of the guitar.

Attempt 2

Johan found out about the Tronicaltune system, a set of motorized tuning pegs for electric guitar that are meant to automatically tune the guitar at your command. The original Tronicaltune system is constituted by six machine heads and a shaft that is attached to the headstock, containing a circuit with a pickup for each string. While strumming, the circuit would compare the sensed pitch to the desired pitch, and adjust the tuning by controlling the motors accordingly.

if (typeof(lightBoxImages) == 'undefined') { lightBoxImages = {}; } lightBoxImages['5470e216a7'] = [{ URL: 'https://hackster.imgix.net/uploads/attachments/1248293/118344290_2655770348073368_8455060937895890907_n_Rmseiir3VY.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: 'Mount of the pegs on the classical guitar, thanks to a 3D-printed holder', type: 'image' },{ URL: 'https://hackster.imgix.net/uploads/attachments/1248291/118200003_1165905337114560_4558036029261135126_n_atTH7qhu6m.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: '', type: 'image' },{ URL: 'https://hackster.imgix.net/uploads/attachments/1248294/118168486_304737484132281_8175749027742726216_n_fGYQ5Xuzrp.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: '', type: 'image' },{ URL: 'https://hackster.imgix.net/uploads/attachments/1248292/118227316_3224079387681432_4603359977744250564_n_Chr5qyC6Bq.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: '', type: 'image' }];

We found and bought a heavily discounted Tronicaltune set, and tried to insert its motorized pegs into the classical guitar, discarding the original circuit and hacking the machine heads. In order to hold the machine heads in place, I designed and 3D-printed a special piece which you might still find useful: you can download it from Thingiverse. Still, the wood of the guitar was amplifying the noise from the stepper motor big time!

Attempt 3 and final setup

The final resolution was to buy an electric guitar. The body of an electric guitar is not made for mechanically amplifying the sound; there is no sound hole. I bought a Fender Squier from Thomann (full model name: Fender Squier Affinity Strat HSS IL SS).

The most delicate process was to open the Tronicaltune machine heads. After a test wiring, Johan got rid of the mechanical contact parts (including springs) and soldered very thin wires to the four terminals of each stepper motor. The four wires needed to be connected to the four outputs of each A4988 driver board.

if (typeof(lightBoxImages) == 'undefined') { lightBoxImages = {}; } lightBoxImages['a72c0550f8'] = [{ URL: 'https://hackster.imgix.net/uploads/attachments/1248295/118280605_171827467761558_5363916362373850403_n_cPBF2RhRWP.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: 'Test wiring', type: 'image' }];

The easiest part was to connect the Arduino to the A4988s. See the schematic for checking how I decided to hook up the wires. We also made some tests and verified that the stepper motors were working at 12V.

if (typeof(lightBoxImages) == 'undefined') { lightBoxImages = {}; } lightBoxImages['e987e0e6e7'] = [{ URL: 'https://hackster.imgix.net/uploads/attachments/1248297/schematics_rondo_3DLlcspKSE.png?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: 'Schematics', type: 'image' }];

Mounting the motorized tuning pegs on the Fender didn't require the 3Dprinted holder, but needed some extra sorcery on the guitar. I actually used some Prusament cylinders from the previously 3Dprinted holder in order to safely rest the pegs in the peg holes of the guitar..

Video and some pics of the mounting process

Software

All the software I wrote was meant to make a DAW (I used Reaper), providing MIDI signal, able to control the motors in real time. Reaper was outputting MIDI notes to a Max MIDI port. Max was listening to that port and converting the MIDI notes to numbers, which were sent via Serial to the Arduino. Finally, the software in the micro-controller would convert these numbers into digitalWrite commands for the motors.

Let's see how these three steps actually work.

Reaper sending MIDI notes

A piano-roll of the MIDI item shows us MIDI notes. I decided that any note equal or above pitch 64 (E4) would make a motor turn so that the corresponding string would tighten up (glissando upwards). Any note below 64 would make the string more loose (glissando downwards).

if (typeof(lightBoxImages) == 'undefined') { lightBoxImages = {}; } lightBoxImages['0c0fbf3590'] = [{ URL: 'https://hackster.imgix.net/uploads/attachments/1248636/schermata_2021-01-25_alle_00_03_44_ATElxpH7qZ.png?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: 'Reaper piano-roll with MIDI notes sent to three different channels.', type: 'image' }];

For each motor I choose a different MIDI channel (chosen based on the colors Reaper had to offer for those channels πŸ˜ƒ): channel 8 for string 4, channel 12 for string 5, channel 12 for string 16 (yes, I motorized only three strings).

On top of this, there is a global control for motor speed, which I never used when I wrote the music for this special guitar, but still present in the software. This was sent as Controller 1 (any channel, 0 = incredibly slow; 127 = maximum speed).

Reaper was also playing back a scrolling video-score for the guitarist to read, so that the playing and the motor control would always be perfectly synced.

Max parsing MIDI events and sending them to the Arduino

The Max patch I wrote had the main function of parsing the MIDI events received from the DAW (but actually you could even connect a MIDI keyboard and control the motors live, for example, or use Max itself). Max was first routing MIDI note messages as per channel (8, 12, 16 for the guitar strings 4, 5 and 6). Then it was checking the pitch and velocity of the note event. MIDI pitch bigger/equal or smaller than 64 was critical to understand if the motor needed to move in one sense or another (direction), MIDI velocity was used to understand if the motor needed to move at all (velocity bigger than 0, which means "note on") or to stop (velocity = 0, or "note off"). Basically, Max was handling seven numbers, sent as bytes via serial to Arduino (with two bytes of value 255 and 250 before and after in order to be sure to distinguish the start and the end of the byte list (is it technically called array? 😨):

  • Channel 8, velocity of the note, to start/stop the motor for string 4 (Arduino sketch: byte m1on)
  • Channel 8, pitch, for the direction of string 4 (Arduino sketch: byte m1sp)
  • Channel 12, velocity of the note, to start/stop the motor for string 5 (Arduino sketch: byte m1on)
  • Channel 12, pitch, for the direction of string 5 (Arduino sketch: byte m2sp)
  • Channel 16, velocity of the note, to start/stop the motor for string 6 (Arduino sketch: byte m3on)
  • Channel 16, pitch, for the direction of string 6 (Arduino sketch: byte m3sp)
  • Controller 1, any channel, for the speed of all motors (Arduino sketch: byte globalSpeed).

Another useful Max patch function is that of assisted tuning: by clicking the UP and DOWN buttons for each string, you can retune the guitar as you like, without the need of turning the pegs with your hands. A monitor section was showing the pitch currently played, for the performer to see if the string needed to be tuned up or down, as in a common tuner.

if (typeof(lightBoxImages) == 'undefined') { lightBoxImages = {}; } lightBoxImages['581184598e'] = [{ URL: 'https://hackster.imgix.net/uploads/attachments/1248298/schermata_2021-01-23_alle_23_06_58_WV9GYjntjw.png?auto=compress%2Cformat&w=1280&h=960&fit=max', caption: 'Overview of the Max patch', type: 'image' }];

Copy and paste the following code in a new Max patch, and the patch will magically appear:

<pre><code>
----------begin_max5_patcher----------
3718.3oc6crsaiab8YueEDB4Qsxy8KaeJooX6h1jFjsoo.IEKnEosYBEo.E0
dKH4auCmgRhxZH4HqgTV6Fa.IQJJdlyk4baNmg+1ytZxM4uOd0jfWD7SAWc0
u8rqtReppSbU8wWMYQ36mmFtReYSlmuXQbV4joluqL98k5y+Mu5qe0ymmmUV
jmlFGEDmFOurHYdvcqSJCKBJWmkjcWvx36VE71jvfurHZcRV9l6SZRV7770Y
5aFp9jKKhWofUXYRd1aJT2Oy.ExmAlFPmwAbAkRADpfCIjoALIn5av3Yff+W
88HIRO7xu4WdNEuAZ2pFmqR9XrFXD0uwb5r0KRxRiK0XJzxfn44UWb95xMWM
nwcNKbg9NO4KKRBSC9p7znM.dYX476UzgFXCRHlgnpAOXeDhJlFPvUCtpuRg
PU2ge+YOq5koNxrVDuZU3cwGvrturb4Kt9528t2MKLs5ZxhJxWFWjjkLSwfm
zM8mPvZxrr5UDfpeqIMuBNyySyKLWuZzCoLACwITfT.TbJMeRgvMOMVNM.1B
mCYiyAQ14bnihyAsw49mqmmDEF7xBEkItClGaCySfaiXX.U4GVFa9ISlr8qt
qHLJI1HyuASt4t8nbBl5O9zV9zNnbyc2ljlp+ouYCr1c+sbQ5WgmBXzuh1xh
UbQn9hQlOAqnDvt9sa9onoMdAZ85CWWluG0ZFW5w4C+ketPccAogqyle+ME4
uaUbQvW.6T4TSwSgMwSY2Rm2mDEEerxk+m3hnvrvNkHkZIRNUKQBgZMHXPOR
jOBhYaVBdYZ9MJ0dudYbbTOZRvB3L0XkH0uAAHyjH6JAH7ImS8zXLyLV0CRL
BrarddHdHIsZ.gPlYZcS7vxIOELxUOJGJp22jmkTluwYiWzmzmTOEgSmAgRo
fJALFQMRqTdIMSaZwdDla0SB3LNhhfHLGodmInmU4UngjKs4WgftC87I8W4u
Wd1yyu8Vqj9iwIOJjYTbokTnxtbrC4SgaajRifBkpc7DROMg23OFFM+Ol3F5
qT5HD..j.jXLkCT9HQz7tpWsQKfLOPKPsSKnb8LXlbyf3ACkCLtrJ4trvzIS
29I+JwoB6HXYhZX9BeIxgL5n5QjiNvhbbgdPPP9PjqM+d9BnRiLXhEzytTDp
SOUrfFBrY5KfrSbAg7uuHYwuSMrO.+p39JeEsgfXqHH9XmNHHZuBnXsFBBzA
DTMS3lvr6djH5pzjn3hIachzHYOoWScLiSBBK15.3YLo5egxLAChQUmDwwy3
L.PcffohtVYSyp1F9NKPEJySkwEuINK7lz3lx2mPzYntlhnI1DwLpBk3DH.p
PBrTM5U9nIEJibf5+phGAoj9HJUoHpPpDKwTJpclTEYcp4scn8cY4JnmlL+W
0iWOpE6u+wW3l8.nNaE05lZ0WOw.qahYLGh7h4vaSyUP1QzmTGTCqSuyXtHT
tmmaftyAzs4p3C0CElujd2n3fo0yKjGfPco3XGvhhmmrHLcYZ3bSd7f9S64h
x7asn5DS5T7xcaCHssMhgBfQirsgUwoAOWI7Zy7GFeDl+5fIyM5nHFelnh9Q
QMCdpuQ0pnISSVzhwdLzOF6UFPLFA0uYNXHXnGiFCjIKkVle0j..t.TXTSco
l.hdRpv35eXUbwpq+uIYIW+5OjM+Ge02csxaZva9aouTuJ.uYQtJ77UWqhDX
w5rn+X1h2ma7d20TsAEGgxmtnlRiVGhIySByQcFHiRrKrz6yMqhEL.ZyCU+n
kkCLYlV3rG3mRXZs4GddXTULHJ+W3VPUneP0MwmalnTmQdz3k3yuOddbxaii
BhBKC6ygbs1IhJtHZuYaBBAW.oaRImYx673kto+5844qhC9gW+UAKyKJ8PB.
L1oMdzVErj9nV3J.4SetRmKt3PkDvvr0pQV8ZAGnhgJt3Vk0l9RmsYVK7vk0
ESDcN8.Ht.XDbyzCxHN83q+W+321CQu1vGhqI9RSXkj1nyLqqBqXvV+7u4CJ
RaTv2Uj2D29k0qJStMY9A2QKjcBhpI6HyBoaAAq.nV5rwM5AqAqThZt90S0m
ho3hDLTIbQIxpSA0eB.q+F8xay8J+7G9t93ll0lBJzFWLGzN2jeAxMMqsYcJ
GrffWPbSWlcV66layNIWb7SLA74zrS.+HlcRu.4ljOulcdLlNwWbrSD8yISm
niwxI5BjYdAZ4rsUsDB.sjISozOYxDy3Fu0qWE1AK2zsghXP.1FB1.sOMLDI
Ey353Q3MpkyQDC6fIx8LSjvdZwDkBOwCEHCOzXI5IEOj4YdHj9DiGR8DOT49
fB+vlzyN9rPq3F1S3lobHpSeiJ1M5vfaquYcYoxDXe4WxrfPlkPlY7Uk2RNk
DxittFNN67jNhxkXRUDxDufsQZqKa31kO7gEuPQ95rnXMBh2s1U2GlEUUrd5
JHYvn7PSEp5FkWBNeTdLU7oEkGeDh7R34ivuI6nexP3qU1.MEpSeJaDmekMP
D9SJkMtQ44hyuxlOYn73iPjGgN+Jat.H7skNiW+u+9W8suLfDr4u5SPCBd3o
XMN0O2qZKybGCgotIMvsjFDNZxSgETSvOXr9HHz6WUssVDslXloDRMvpZuh1
jvICrDdWqShTpGnBSmxZYf5IOuchrg3Z36FUCbFoZXcnXOMHZPNRKd6DUCdF
oZX0fB8jgrwp6ZBZ+DM5YjlYxiwfSxTCna5WqFrYzYzCqZyC6l6oMdoEpKaf
otndKCr50sfBNpVxnkLe2Hg1PrtmnkbFgCnBARwMk5VZeu7dCI0zF+UEcKKR
14KPyFi8XJUvNpqeldVifidX8X6OTP4lTYr0xcDi7SIWWO2hY36lT+1O22qU
63GSCJheqsR5T3IbzjQMFTW7p08Ay3hiKCm+q1vPek6dCFR4xMt0MxY1dGhz
QGUUkXZFrEsLMoJcWf8dweucs7OELxcRw7xzjLaKVk7HzKg6pQuzRAa5wNGP
uFsu01.07or+uVs53Af89GQskKcg8IDxikECIPiK8TSAqObsRYawbt.tMNxF
eTc.pkOiadQ3eNKOa2wQIEM91leSWek96beGC5XJ8255wttRLQctOBInm0He
glcs.A9fgpGU.xYts6S3j9OAdX0+gTtrn0+IGV8e8tHZsQqT9V6LshLvzJD3
rRqXhdKhUn6DqA1vJTEO4SYAKHV5NsBMvzJH6rRq38IXI3lpkvIhEXXIVLhd
r7jUvBScmVwkCrfEZbnUs43SYdfosECp1EABByhBvV7ziC7ST3PSFsYXs80S
s65awE10YsD.Gi3m9qr1LCSV2.zGkO6iClh8TmjZTqvDnmrXJxSXJBaPUSKH
eFQ0VyvBC5mLrTuOkwL6miCaFVZUGcvNSZMQQeUAnl08jgzt3L5aMOlLAJTZ
2C1skU0L871UDQN1U6cCmznNkKcjSNcH3nLQ.Pgvt1q6DOUmnFwWJmsK8Ic2
o6dOSglTjXK00jiXaXB1KWtd2TABHirzbVdYr0bfQ.9IGXaPPSNvbBA8TRvZ
sFDhSm8Oh+vM4gEQ1PboebGBS4FGIfC4Fg1s8sahHLNBa1MUsTJqMS6IZnif
moMCinCaY01FspuF4lRXlkt1IhEcfCdv3M84hVEMwscb.WHUhAlRQ3mUwp4t
MEjTyP6jTMv4PqdmgXnoTcrYpXpCsCWDUpe1IULEcREZZ16HbaaqxqhCssX2
PhebJpFEofgcAFaco7iWFmEs21VRSjD6G9nwyOtYACF8dK4FGMT3zTZ3.mpW
dcqneLyosrM92RqSpqVFkyap+z8HotKJe31i+9an9HcsjTc03sOzDvUmB191
p+iXm+2oQVaa5+Olw39a8+OhafRUioLhLsdsKOr.rcUKKxql8UKun.qOefBD
5lcemj7G3zqV2iHmGaYqh0q34MUM.m88lPqsZcOOxVrmlmCWo0tVjUD1ryb0
V282V3VSr1Q0fGAIaYXVbZe4omq4XlJ2BYdCiayqnsDyaxKhhK1alX8ifkMc
3sVqvdObVzOfNv6+29SRGlcaVpIiEDyNdHDI191V.uHOZu9V2pFhJMBa05wZ
fp0O2RN3YQiBaIPXix7aers59sUQH9f1fuIkCR0zS7tqvn0q4s6gpinCjDCD
rSfop6Z6TfgdIJvXvv58Bv+TbwGhKXiBPByXqfIevCk.qBOrKQgGiZebcuL8
mROmlzS09mgfuS.Bpz6vztY6jHztJp6RRDh.LOWdjhOakgzWjd8MdvyVPM4p
576KXsJecw7MNRs4A9VvNXEEupLIaKK4m18X2pwE03ApkUwYWgc0lHSuvtZc
xCZKGzNCIgKPB6CHwGMHwbARHe.I5nAIrCPppRea7zqSqvROkdj.M9PPCGIP
SODzfSCzv8nk1A8F9+oCo9PRoWlX.bAmD9BRiCN4.ff9.PB4XgRZH0GaRuoG
c5fR3hDgzWPZbHetXngru0cOouxIPisC4SSckXrlFvcRhg6KHMJRLa7j5QYl
Ac5fF5BnY9VX0MrlX2kB3Hf0X6fFL7XMBLDyQYmOGoXXWEyHmGrFMDdt5FVC
GDIbWvZ3vHlgNgPEfmNncRLCMHXcufFLXXceDbtbPjvcJIEChTlKQsP8QJJX
tJNyOQ.oGsnwHqKTWr+4kbgPIGQFJ7qDhafdPlQRbBzvg.qcBzDwff0tDzMw
GxuDWRj.oVH+zgDxEHgNUHMVQPgcIogPevlvN4qKyGPxEOsdvvwSS3bCzfgH
bem.MjaGzmlgeL5HrJ5Yr1EPifCQvaXWb5.6iIoHWR9.1GY1G4RNiP9HorHW
7jB4EpmKQ+f8ghGjKt8K8FjFkzQgbQJG4CCDtfRPeH5AcI44HtufDzEHcptl
.cREAzG3Dcr7MARbOA1mFfviEfPiFsCNZPB.Fq5CvEb5PsqlhuHb4x2FWrp9
h0fXxhvewT+Lho5CSxLGpKCkIEwuMYy0qan1IgEyuOoLdd45BSUy995sG.cA
rTjsNo1gFExo.ot1fppv1UKqK6UcID8re+Y+e9W7hwB
-----------end_max5_patcher-----------
</code></pre>

And finally, the Arduino sketch

The Arduino sketch (you find the whole sketch in the attachment section) is taking care of receiving the previously mentioned bytes and transform them into commands for the three motors. A serial connection is established

Serial.begin(38400);

and bytes are received:

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

Then, bytes are parsed so that we end up with 7 numbers indicating the commands. Every time the 7 bytes are collected, they are shown in the serial window, just for debugging:

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;
}
}

The raw commands are converted into motor commands sent to the respective pins.

/// 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);
}

This part above checks for each note velocity if it's equal to 0. If it is, the motor is stopped and the A4988 is put into sleep mode (very important for avoiding overheating!). If not, the motor is started.

The next part checks the pitch of the note events against the value 64, and decides the direction of rotation:

/// 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);
}

A further section examines the controller value and scales it to obtain the interval, in microseconds, between each step. And then uses that value as a pause between the steps:

int scaledSpeed = (lowestMd + 127 * 25) - globalSpeed * 25;
delayMicroseconds(scaledSpeed);
digitalWrite(step1, LOW);
digitalWrite(step2, LOW);
digitalWrite(step3, LOW);
delayMicroseconds(scaledSpeed);

If you look at the void loop() section, you can see that the actual operation routine is:

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

Which means: receive the bytes, write the commands to the motor pins (after having interpreted the bytes), step the desired motors, wait for an interval of microseconds calculated as scaleSpeed, put all three motors to sleep, show the values in the serial window, repeat the loop.

Tips:
  • Don't gamble with the MIDI commands: you will break the strings if you stretch the strings too much. Always keep the circuit powered through a power strip with a button, in case you lose control of a motor (this happened to me, for example, when I connected a USB device to the computer, and the USB ports changed just after a note-on event).
  • Don't run the motors continuously for a long time, they overheat and this may cause problems to the motors.

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.


*/

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.
Schematics rondo l8yhtgcld6

Comments

Similar projects you might like

DrumCube, an Arduino Robot Drummer

Project showcase by FrancoMolina

  • 18,918 views
  • 28 comments
  • 100 respects

Guitar Speed Pick and Stomp Pedal!

Project tutorial by Marc Uberstein

  • 8,965 views
  • 9 comments
  • 36 respects

Self-Playing Melodica πŸ€–πŸŽΉ

Project tutorial by touchmysound

  • 4,339 views
  • 2 comments
  • 30 respects

AiRobot

Project tutorial by Blaine Ayotte and Tyler Bershad

  • 4,201 views
  • 1 comment
  • 14 respects

Control Music Volume and Ledbar Using Slider of 1Sheeld

Project tutorial by ahmed ismail

  • 3,359 views
  • 2 comments
  • 14 respects

Arduino UNO Guitar Pedal

Project tutorial by electrosmash

  • 91,825 views
  • 32 comments
  • 241 respects
Add projectSign up / Login