Machine making week

This is part two of the two week assignment.

Go to part 1

For the second week we should automate the machine. David compiled the functional code, and I wanted to make some additions to it but unfortunately couldn't find the time for most. However I did make a couple of minor adjustments, making the pauses more modular to fit the beat of the song, which has a consistent beat pattern, and at times makes a pause for the full length of a beat, sometimes a pause for half a beat, and sometimes a pause for two full beats, and turned the motor driving line into a function.
My changes can be basically concluded thus:

void empty_beat(int val){ delay(musicPulse * val); } void full_beat(bool dir, int val){ step(dir, motorX_DIR, motorX_STP, val); } void line(bool dir, int val){ step(dir, motorY_DIR, motorY_STP, val); }

And some fixing of values into variables.
David also included in the code that for each motor, 50 steps is 1 cm, which is great for me because the A3 paper is 297x420mm. So with 50 steps per 10mm, it's 5 steps per mm, and I can easily extrapolate the right values for the paper to stay in rhythm, since I divided my paper in photoshop to a 10x10 grid:



With this being basically the map of the movements:



Where 1 stands for 297/10 mm - 29.7mm.
Since it's 50 steps per 10mm, it's easy to find how many steps per 29.7mm. 50s = 10mm means that 5s is 1mm, which means that:
5s*0.7 = 1mm*0.7 = 3.5

Unfortunately 3.5 is not a valid step size, so it looks like I have to round up the 29.7mm to 30mm. So it's 30mm per grid block, which is easy -> it's 150 steps per 30mm. Tweaking the code a little to accommodate:

int musicPulse = 150; // music timing pulse for the frere jacques functions int line_roll = 150; int step_size = 150; int initial_step = 185; void empty_beat(int val){ delay(musicPulse * val); } void full_beat(bool dir, float val){ step(dir, motorX_DIR, motorX_STP, int(val*step_size)); } void line(bool dir, int val){ step(dir, motorY_DIR, motorY_STP, val*step_size); } Which puts the use of the functions to a convenient: void frBackwards() { //initial step to the start of the page //fre full_beat(true,initial_step); empty_beat(2); for (int x = 0; x < 2 ; x++) { //re full_beat(true,1); empty_beat(2); //ja full_beat(true,2); empty_beat(2); //cque full_beat(true,2); empty_beat(2); line(true,line_roll); empty_beat(2); //fre full_beat(true,1); //re full_beat(false,1); empty_beat(2); //ja full_beat(false,2); empty_beat(2); //cque full_beat(false,2); empty_beat(2); line(true,line_roll); empty_beat(2); //dor full_beat(false,1); empty_beat(2); //mez full_beat(true,2); empty_beat(2); //vous full_beat(true,3); empty_beat(4); line(true,line_roll); empty_beat(2); //dor full_beat(true,1); //mez full_beat(false,2); //vous full_beat(false,3); empty_beat(4); line(true,line_roll); empty_beat(2); full_beat(false,1); //son full_beat(true,1); //nez empty_beat(1); //les full_beat(true,1.5); empty_beat(1); //ma full_beat(true,1.5); empty_beat(1); //ti full_beat(true,1); empty_beat(2); //nez full_beat(true,1); empty_beat(2); line(true,line_roll); empty_beat(2); //son full_beat(false,1); //nez empty_beat(1); //les full_beat(false,1.5); empty_beat(1); //ma full_beat(false,1.5); empty_beat(1); //ti full_beat(false,1); empty_beat(2); //nez full_beat(false,1); empty_beat(2); line(true,line_roll); empty_beat(2); full_beat(true,0.5); //ding empty_beat(2); //ding full_beat(true,2.5); empty_beat(2); //dong full_beat(true,2.5); line(true,line_roll); empty_beat(2); //ding empty_beat(2); //ding full_beat(false,2.5); empty_beat(2); //dong full_beat(false,2.5); }

This should, hopefully, run the entire song in rhythm. If not - some adjustments will be made on the spot. Next is to print the actual song on a roll of paper.
I converted my jpg image to an SVG vector file with the use of this website:

http://www.pngtosvg.com/

At the lab, Emma gave me a big roll of very wide paper to print on using the vinyl cutter. Imported it into the Illustrator we have at the lab, and printed it using our vinyl cutter by attaching a marker to it instead of a knife.

I set the page settings to be 299mm x 5000mm (the 5000mm is just for a bit of extra length to play with, and just opened my SVG in another file. I then:

Copy pasted it into my main page, which is 299mm x 5000mm
Resized it to be 297mm in width (maintaining the proportions)
And drew a rectangle around it too (a long one) just to be able to print a long rectangle as well.
Then I just sent it to print.
I used this rectangle as a marker to cut out the correct shape from the wide paper.
Here it is printing:

Here is the printed sheet:

And after I have cut out the rectangle I printed to resize the paper:

It was at this point that I realized my paper is actually reversed. That is, the code unrolls the paper down, but my printed paper is supposed to be rolled upward to follow the lyrics correctly, so I changed the code.

I then attached the roll of paper with duct tape to the karaoke machine, and stuck a metal stick to the other end of it (also using duct tape).

Oh and of course I also hooked up the power supply to the board. Everything else was already hooked up by Xavier and David, so here's my contribution :P :

After a few trial and errors with the rhythm and order of the movements on the machine, I kept tweaking the code a couple of times until I ended up with the following code.
Note - this is the full and final code:

/* Fab Academy Karaoke machine * David McCallum, Xavier Klein, Dima Paley, 2018 * sintheta.org * Code adapted from http://aconcaguasci.blogspot.nl/2016/11/arduino-cnc-shield-control-stepper.html * * For both X and Y, 50 steps per cm * * */ // Motor stuff const int ENABLE = 8 ; const int motorX_DIR = 5; //Direction pin const int motorX_STP = 2; //Step pin const int motorY_DIR = 7; //Direction pin const int motorY_STP = 4; //Step pin const int endstopPinX = 9; //Driver Timing int delayTime=450; //Delay between each pause (uS) int musicPulse = 150; // music timing pulse for the frere jacques functions. This is the beat! int line_roll = 1; // How many "line_step" steps to take up or down int step_size = 150; // The size of each step to the left or right int line_step = 350; // The size of each step up or down int initial_step = 3; // The size of the initial step to take from the start position // The basic move function void step(boolean dir, byte dirPin, byte stepperPin, int steps){ digitalWrite(dirPin, dir); for (int i = 0; i < steps; i++) { digitalWrite(stepperPin, HIGH); delayMicroseconds(delayTime); digitalWrite(stepperPin, LOW); delayMicroseconds(delayTime); } } // Move the motor until the endstop is reached void goHome() { while (digitalRead(endstopPinX) == HIGH) { // if the button isn't pressed, move the motor 1 step step(false, motorX_DIR, motorX_STP, 1); delayMicroseconds(delayTime); } step(true, motorX_DIR, motorX_STP, 50); } // This is a function to wait for a beat (or two beats, ore more) void empty_beat(int val){ delay(musicPulse * val); } // This is a function to move the finger motor left or right void full_beat(bool dir, float val){ step(dir, motorX_DIR, motorX_STP, int(val*step_size)); } // This is a function to move the paper roll motor up or down void line(bool dir, float val){ step(dir, motorY_DIR, motorY_STP, int(val*line_step)); } // This is the full song function // play Frere Jacques with alternating lines moving backwards (because the motor couldn't rewind fast enough) void frBackwards() { //initial step to the start of the page //fre full_beat(true,initial_step); empty_beat(2); for (int x = 0; x < 1 ; x++) { //re full_beat(true,1); empty_beat(2); //ja full_beat(true,2); empty_beat(2); //cque full_beat(true,2); empty_beat(2); line(false,line_roll); empty_beat(2); //fre full_beat(true,1); empty_beat(2); //re full_beat(false,1); empty_beat(2); //ja full_beat(false,2); empty_beat(2); //cque full_beat(false,2); empty_beat(2); line(false,line_roll); empty_beat(2); //dor full_beat(false,1); empty_beat(2); //mez full_beat(true,2); empty_beat(2); //vous full_beat(true,3); empty_beat(4); line(false,line_roll); empty_beat(2); //dor full_beat(true,1); empty_beat(2); //mez full_beat(false,2); empty_beat(2); //vous full_beat(false,3); empty_beat(4); line(false,line_roll); empty_beat(2); full_beat(false,1); empty_beat(1); //son full_beat(true,1); //nez empty_beat(1); //les full_beat(true,1.5); empty_beat(1); //ma full_beat(true,1.5); empty_beat(1); //ti full_beat(true,1); empty_beat(2); //nez full_beat(true,1); empty_beat(2); line(false,line_roll); empty_beat(2); full_beat(true,0.1); //son full_beat(false,1.1); //nez empty_beat(1); //les full_beat(false,1.5); empty_beat(1); //ma full_beat(false,1.5); empty_beat(1); //ti full_beat(false,1); empty_beat(2); //nez full_beat(false,1); empty_beat(2); line(false,line_roll); empty_beat(2); full_beat(false,0.1); full_beat(true,0.6); //ding empty_beat(2); //ding full_beat(true,2.5); empty_beat(2); //dong full_beat(true,2.5); empty_beat(2); line(false,line_roll); empty_beat(2); full_beat(true,0.1); //ding empty_beat(2); //ding full_beat(false,2.6); empty_beat(2); //dong full_beat(false,2.5); empty_beat(2); } } void setup(){ // serial.begin(9600); pinMode(motorX_DIR, OUTPUT); pinMode(motorX_STP, OUTPUT); pinMode(motorY_DIR, OUTPUT); pinMode(motorY_STP, OUTPUT); pinMode(ENABLE, OUTPUT); digitalWrite(ENABLE, LOW); pinMode(endstopPinX, INPUT_PULLUP); goHome(); // zero the motor to the edge delay(5000); frBackwards(); goHome(); line(true,7*line_roll); } void loop(){ }

Explanation of the code:
Because I divided the paper into 10 parts like this, and mapped each movement, and because each 1/10th of the paper part is approximately 3cm, and 50 steps moves 1cm, the reasoning is as follows:


In the code, each step is expressed with this variable:

int step_size = 150;

And the movement itself is expressed with this function:

void full_beat(bool dir, float val){   step(dir, motorX_DIR, motorX_STP, int(val*step_size)); }

Which takes two arguments. The direction (true for ->, false for <-), and how many steps to take. The value is float, so you can say "0.5 steps" to run it for 1.5cm, or 1 step to run it for 3cm, etc.

Next is the pause.
Frere Jacque has a fairly consistent beat, and the song rhythm moves according to this beat. The song is sang at either the exact speed of the beat, at twice the speed of the beat, or at half the speed of the beat, depending on each part of the song.
Because the beat is consistent, it was expressed in the code also as a modular, easy to use function:

int musicPulse = 150;     // music timing pulse for the frere jacques functions. This is the beat!

This variable already existed in David's original code, but is now used in this function:

void empty_beat(int val){   delay(musicPulse * val); }

The actual beat itself is 300. I set the variable to 150 because I'm using the half-beat as the base point. So in the actual code itself, you'll see it like this:

full_beat(true,1); // Moves the hand motor one step (3cm) to the right empty_beat(2); // Pauses the hand motor for 2 halves of a beat of the song

Next is the line rolling. I printed the paper to roll the song up, rather than down.
The space between each line is approximately 7cm, so the line roll step size has it's own variable:

int line_step = 350; // The size of each step up or down

And it's own function:

void line(bool dir, float val){   step(dir, motorY_DIR, motorY_STP, int(val*line_step)); }

Again, it takes two arguments, the direction boolean (true is down, false is up), and the variable for how many steps to take. I already pre-set how many steps to take with a pre-determined variable:

int line_roll = 1; // How many "line_step" steps to take up or down

So the actual function is simply called like this:

line(false,line_roll);

I pre-set the variable because I was tweaking it a lot, and it's basically just one value (as opposed to being different variations of values like with the other functions), so it was easier to keep tweaking by simply using a variable, rather than changing it in every instance the function is called. Basically this way, setting up the song to run is much more simple, with the code just looking like this:

line(false,line_roll); // Roll up the paper one line empty_beat(2); // Wait for one full beat (two halves of a beat) //fre full_beat(true,1); // Move the motor one step to the right empty_beat(2); // Wait for one full beat //re full_beat(false,1); // Move the motor one step to the left empty_beat(2); // Wait one full beat //ja full_beat(false,2); // Move motor two steps to the left empty_beat(2); // Wait one full beat //cque full_beat(false,2); // Move motor two steps to the left empty_beat(2); // Wait one full beat

Etc.
There are instances in the code where the amount of steps the motor is moved is 0.5, or 1.5, etc.
The song is also documented in the code - that is, the part where it should go "Fre" is commented, the part where it should go "ja" is commented, the part where it goes "cques" is commented, etc, so that calibration and tweaking is easier to do.

Anyway, after all the tweaking, a miracle occurred:

It worked! And worked really well too.
Not perfect, but really well.

And that's it!

The files:

The photoshop song image (rar archive)
Song SVG
Machine DXF cutting path
Fusion 360 file of the machine (model and sketches)
Arduino sketch / code

This is part two of the two week assignment.

Go to part 1

Back home