Why can Step Motor Create Music?
When step motor moves with a specific speed value, it creates the vibration with a specific frequency, the vibration make the surrounding environment oscillate. The oscillation creates a mechanical wave. Frequency value is depended on the speed. When frequency value is between about 20 Hz and 20 kHz, it becomes sound wave and our ear can hear it.
By adjusting the speed, we can create the different frequency of sound. Therefore, we can create a different note of music.
By mapping music notes of a song to speed, moving step motor with the speed values in sequence, step motor can create melody of that song. We can use more than one motor to play a complex song.
Different material connected with motor will create the different timbre.
Things Used In This Project
- PHPoC Blue or PHPoC Black (P4S-342 / P4S-341)
- Two Step Motor Smart Expansion Boards (PES-2403)
- Two step motors
- XY-Plotter (Optional)
In this project, I use two step motors installed on XY-Plotter. Why I use the motors install on XY-plotter. Because I have already had it and also want to hear timbre of XY-Plotter. You can install two step motors to any material to hear it timbre or even we don’t need to use any material.
With two motor, we can create melody and chord.
In demonstration video, I use the step motor to play "Marble Machine" song.
Assembly
- Stack two step motor smart expansion boards on the PHPoC board.
The ID of this expansion board is set to 13 and 14 by adjusting the dip switch position as shown on bellow image
- Refer to specification of your step motor to see how to connect step motor to step motor smart extension board. In my case, I use 42BYG Stepper Motor.
The connection as follow:- Board ---------- step motor
- M1+ ----------- black
- M1- ------------ green
- M2+ ----------- blue
- M2- ------------ red
- GND ----------- DC12V -
- VM -------------- DC12V +
Source Code
task0.php
PHP Code:
<?php
include_once "/lib/se_step_play.php";
function play_marble_machine_song()
{
global $step_play_dir;
// test
$melody1a = step_play_encode("5E2.67 4B2 4A8 4G8 4A4 4B4 4G8 4A8 5D8 R2.67 4B2 4A8 4G8 4A4 4F#4 4G8 4A8 5D8");
$melody1b = step_play_encode("R2.67 4B2 5D8 5C8 4B4 4A4 4G8 4A8 4E8 R8 4C8 4E8 4B8 3B8 4C8 4D8 5D8 5C8 4B4 4A4 4G8 4A8 5E8");
$melody1c = step_play_encode("R2.67 4B2 4A8 4G8 4A4 4B4 4G8 4A8 5D8 R2.67 4B2 5D8 5C8 4B4 4A4 4G8 4A8 5D8");
$melody1d = step_play_encode("R2.67 4B2.67 4A8 5E8 R8 4B4 4A4 4G8 4F#8 4E8 R8 3B8 4C8 4F#8 4C8 4E8 4G8 4D8 4F#8 4A8 3B8 4B8 4D8 4G8 4A8 5E8");
$chord1a = step_play_encode("3E4 4E2.67 3E8 4E4 3E4 4E2.67 3E8 4E4 3G4 4G2.67 3G8 4G4 3G4 4G2.67 3G8 4G4");
$chord1b = step_play_encode("3D4 4D2.67 3D8 4D4 3D4 4D2.67 3D8 4D4 3C4 4C2.67 3C8 4C4 3D4 4D2.67 3D8 4D4");
$chord1c = step_play_encode("3E4 4E2.67 3E8 4E4 3E4 4E2.67 3E8 4E4 3G4 4G2.67 3G8 4G4 3G4 4G2.67 3G8 4G4");
$chord1d = step_play_encode("3D4 4D2.67 3D8 4D4 3D4 4D2.67 3D8 4D8 3C4 4C2.67 3C8 4C4 3D4 4D2.67 3D8 4D4");
step_play_dir(+1);
step_play_harmony($melody1a, $chord1a);
//step_play_melody($melody1a);
step_play_dir(-1);
step_play_harmony($melody1b, $chord1b);
step_play_dir(+1);
step_play_harmony($melody1c, $chord1c);
step_play_dir(-1);
step_play_harmony($melody1d, $chord1d);
}
spc_reset();
spc_sync_baud(460800);
step_play_setup(0, 13);
step_play_setup(1, 14);
step_play_tempo(38);
play_marble_machine_song();
?>
Library
Two libraries are required for this example. You need the official library sd_spc.php in PHPoC Support Package and the library se_step_play.php for step motor performance.
The library must be located in the /lib folder
se_step_play.php
PHP Code:
<?php
include_once "/lib/sd_spc.php";
$step_play_speed = 1000; // ms per bar
$step_play_dir = +1;
$step_play_sid = array(0, 0, 0, 0);
function step_play_setup($play_id, $sid, $vref = 8, $mode = "half", $accel = 10000)
{
global $step_play_sid;
if(spc_request_sys($sid, "get did") != "40002403")
exit("step_play_setup: expansion not found\r\n");
spc_request_dev($sid, "set vref stop 2");
spc_request_dev($sid, "set vref goto $vref");
spc_request_dev($sid, "set mode $mode");
spc_request_dev($sid, "set accel $accel");
$step_play_sid[$play_id] = $sid;
}
function step_play_tempo($tempo)
{
global $step_play_speed;
$step_play_speed = (int)(1000.0 / ($tempo / 60.0));
}
function step_play_dir($dir)
{
global $step_play_dir;
if($dir >= 0)
$step_play_dir = +1;
else
$step_play_dir = -1;
}
function step_play_encode($score)
{
global $step_play_speed;
$score_array = explode(" ", $score);
$score_count = count($score_array);
$score_encoded = "";
for($i = 0; $i < $score_count; $i++)
{
if(!($note = $score_array[$i]))
continue;
if(strtoupper($note[0]) == "R")
{ /* rest */
$dur_fp = (float)substr($note, 1);
$dur_ms = (int)round($step_play_speed / $dur_fp);
$score_encoded .= int2bin(0, 2);
$score_encoded .= int2bin($dur_ms, 2);
}
else
{ /* tone */
$tone = (int)$note[0] * 12; // octave * 12
// C C# D D# E F F# G G# A A# B
// 0 1 2 3 4 5 6 7 8 9 10 11
switch(strtoupper($note[1]))
{
case "C":
$tone += 0;
break;
case "D":
$tone += 2;
break;
case "E":
$tone += 4;
break;
case "F":
$tone += 5;
break;
case "G":
$tone += 7;
break;
case "A":
$tone += 9;
break;
case "B":
$tone += 11;
break;
default:
exit("encode_score: unknown tone '" . $note[1] . "'\r\n");
break;
}
if($note[2] == "#")
{
$tone++;
$dur_fp = (float)substr($note, 3);
}
else
if($note[2] == "b")
{
$tone--;
$dur_fp = (float)substr($note, 3);
}
else
$dur_fp = (float)substr($note, 2);
$dur_ms = (int)($step_play_speed / $dur_fp);
$octave = $tone / 12;
$tone = $tone % 12;
$freqA = 440.0 * pow(2, ($octave - 4));
$freq = (int)round($freqA * pow(2, ($tone - 9) / 12.0));
$score_encoded .= int2bin($freq, 2);
$score_encoded .= int2bin($dur_ms, 2);
}
}
return $score_encoded;
}
function step_play_melody($melody)
{
global $step_play_dir;
global $step_play_sid;
while($melody)
{
$freq = bin2int($melody, 0, 2);
$dur_ms = bin2int($melody, 2, 2);
if($freq)
{
if($step_play_dir > 0)
spc_request_dev($step_play_sid[0], "goto +1000000 $freq");
else
spc_request_dev($step_play_sid[0], "goto -1000000 $freq");
usleep($dur_ms * 1000);
spc_request_dev($step_play_sid[0], "stop 0");
}
else // zero frequency is 'rest'
usleep($dur_ms * 1000);
$melody = substr($melody, 4);
}
}
function step_play_harmony($melody1, $melody2)
{
global $step_play_dir;
global $step_play_sid;
$pid_st0 = pid_open("/mmap/st0");
pid_ioctl($pid_st0, "start");
$melody = array($melody1, $melody2);
$melody_next_ms = array(0, 0);
while($melody[0] || $melody[1] || $melody_next_ms[0] || $melody_next_ms[1])
{
for($i = 0; $i < 2; $i++)
{
if($melody_next_ms[$i])
{
if($melody_next_ms[$i] <= pid_ioctl($pid_st0, "get count"))
{
spc_request_dev($step_play_sid[$i], "stop 0");
$melody_next_ms[$i] = 0;
}
}
else
{
if($melody[$i])
{
$freq = bin2int($melody[$i], 0, 2);
$dur_ms = bin2int($melody[$i], 2, 2);
$melody[$i] = substr($melody[$i], 4);
if($freq)
{
if($step_play_dir > 0)
spc_request_dev($step_play_sid[$i], "goto +1000000 $freq");
else
spc_request_dev($step_play_sid[$i], "goto -1000000 $freq");
}
$melody_next_ms[$i] = pid_ioctl($pid_st0, "get count") + $dur_ms;
}
}
}
}
pid_close($pid_st0);
}
?>