khanhhs
Published © GPL3+

PHPoC - Web-Based MP3 Player

This project shows how to control MP3 player via webpage using PHPoC.

BeginnerShowcase (no instructions)1,513
PHPoC - Web-Based MP3 Player

Things used in this project

Hardware components

PHPoC Blue
PHPoC Blue
×1
MP3 Player
DIYables MP3 Player
×1
Speaker
×1
Micro SD Card
×1
Jumper Wires
DIYables Jumper Wires
×1
Starter Kit
DIYables Starter Kit
×1

Story

Read more

Custom parts and enclosures

Serial MP3 Player to PHPoC

Code

Web UI (index.php)

PHP
<!DOCTYPE html>
<html>
<head>
<title>PHPoC - IoT MP3 Player</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7">
<style>
body { text-align: center; font-size: 15pt; }
h1 { font-weight: bold; font-size: 25pt; }
h2 { font-weight: bold; font-size: 15pt; }
button { font-weight: bold; font-size: 15pt; }
</style>
<script>
var canvas_width = 450, canvas_height = 250;
var trans_x = canvas_width/2, trans_y = canvas_height/2 - 30;
var plate_width = 300, plate_height = 80;
var volume_width = 300, volume_height = 8;
var circle_radius = 80;
var volume_y = circle_radius + 35;
var inner_radius;
var pause_width, pause_height;
var play_width, play_height;
var next_width, next_height, next_top, next_right;
var arrow_width;

var play_state = 0; // 0: pause. 1: playing
var volume = 20;
var click_state = 0; // 0: no click, 1: back button click, 2: next button click 
var ws;

function init()
{
	var canvas = document.getElementById("remote");
	canvas.style.backgroundColor = "#999999";
	canvas.width = canvas_width;
	canvas.height = canvas_height;
	
	canvas.addEventListener("touchstart", mouse_down);
	canvas.addEventListener("touchend", mouse_up);
	canvas.addEventListener("touchmove", mouse_move);
	canvas.addEventListener("mousedown", mouse_down);
	canvas.addEventListener("mouseup", mouse_up);
	canvas.addEventListener("mousemove", mouse_move);
	
	var ctx = canvas.getContext("2d");
	
	ctx.translate(trans_x, trans_y);
	
	inner_radius = circle_radius - 25;
	next_height = Math.round(0.6 * plate_height);
	arrow_width = Math.round(next_height * Math.cos(Math.PI/3.5));
	next_width = 2*arrow_width - 7;
	next_top = next_height / 2;
	next_right = plate_width / 2 + 15;
	
	update_view();
}
function connect_onclick()
{
	if(ws == null)
	{
		var ws_host_addr = "<?echo _SERVER("HTTP_HOST")?>";
		if((navigator.platform.indexOf("Win") != -1) && (ws_host_addr.charAt(0) == "["))
		{
			// network resource identifier to UNC path name conversion
			ws_host_addr = ws_host_addr.replace(/[\[\]]/g, '');
			ws_host_addr = ws_host_addr.replace(/:/g, "-");
			ws_host_addr += ".ipv6-literal.net";
		}
		
		ws = new WebSocket("ws://" + ws_host_addr + "/remote_slide", "text.phpoc");
		document.getElementById("ws_state").innerHTML = "CONNECTING";
		ws.onopen = ws_onopen;
		ws.onclose = ws_onclose;
		ws.onmessage = ws_onmessage;
	}
	else
		ws.close();
}
function ws_onopen()
{
	document.getElementById("ws_state").innerHTML = "<font color='blue'>CONNECTED</font>";
	document.getElementById("bt_connect").innerHTML = "Disconnect";
}
function ws_onclose()
{
	document.getElementById("ws_state").innerHTML = "<font color='gray'>CLOSED</font>";
	document.getElementById("bt_connect").innerHTML = "Connect";
	ws.onopen = null;
	ws.onclose = null;
	ws.onmessage = null;
	ws = null;
}
function ws_onmessage(e_msg)
{
	e_msg = e_msg || window.event; // MessageEvent
	alert("msg : " + e_msg.data);
}
function update_view()
{
	var canvas = document.getElementById("remote");
	var ctx = canvas.getContext("2d");
 
	ctx.clearRect(-trans_x, -trans_y, canvas_width, canvas_height);
	
	ctx.fillStyle="#404040";
	ctx.beginPath();
	ctx.arc(-plate_width / 2, 0, plate_height / 2, 0.5 * Math.PI, 1.5 * Math.PI);
	ctx.lineTo(plate_width / 2, -plate_height / 2);
	ctx.arc(plate_width / 2, 0, plate_height / 2, 1.5 * Math.PI, 0.5 * Math.PI);
	ctx.lineTo(-plate_width / 2, plate_height / 2);
	ctx.fill();
	
	var gradient=ctx.createLinearGradient(0,-circle_radius,0,circle_radius);
	gradient.addColorStop(0,"white");
	gradient.addColorStop(0.5,"#cceeff");
	gradient.addColorStop(1,"white");
	
	ctx.fillStyle=gradient;
	ctx.beginPath();
	ctx.arc(0, 0, circle_radius, 0 , 2 * Math.PI);
	ctx.fill();
	
	var arrow1_right = next_right;
	var arrow1_left = next_right - arrow_width;
	var arrow2_left = next_right - next_width;
	var arrow2_right = arrow2_left + arrow_width;
	
	// Back button
	if(click_state == 1)
		ctx.fillStyle="#66ffff";
	else
		ctx.fillStyle=gradient;
	ctx.beginPath();
	ctx.lineTo(-arrow1_right, 0);
	ctx.lineTo(-arrow1_left, next_height / 2);
	ctx.lineTo(-arrow1_left, -next_height / 2);
	ctx.fill();
	ctx.beginPath();
	ctx.lineTo(-arrow2_right, 0);
	ctx.lineTo(-arrow2_left, next_height / 2);
	ctx.lineTo(-arrow2_left, -next_height / 2);
	ctx.fill();
	
	// Next button
	if(click_state == 2)
		ctx.fillStyle="#66ffff";
	else
		ctx.fillStyle=gradient;
	ctx.beginPath();
	ctx.lineTo(arrow1_right, 0);
	ctx.lineTo(arrow1_left, next_height / 2);
	ctx.lineTo(arrow1_left, -next_height / 2);
	ctx.fill();
	ctx.beginPath();
	ctx.lineTo(arrow2_right, 0);
	ctx.lineTo(arrow2_left, next_height / 2);
	ctx.lineTo(arrow2_left, -next_height / 2);
	ctx.fill();
	
	var x = Math.round(inner_radius * Math.cos(Math.PI/3));
	var y = Math.round(inner_radius * Math.cos(Math.PI/6));
	ctx.fillStyle="#2eb82e";
	
	if(!play_state)
	{
		// Pausing button
		ctx.beginPath();
		ctx.lineTo(inner_radius, 0);
		ctx.lineTo(-x, y);
		ctx.lineTo(-x, -y);
		ctx.fill();
	}
	else
	{
		// Playing button
		x -= 3;
		y -= 3;
		var bar_width = 14;
		ctx.beginPath();
		ctx.lineTo(-x - bar_width, -y);
		ctx.lineTo(-x - bar_width, y);
		ctx.lineTo(-x + bar_width, y);
		ctx.lineTo(-x + bar_width, -y);
		ctx.fill();
		ctx.beginPath();
		ctx.lineTo(x - bar_width, -y);
		ctx.lineTo(x - bar_width, y);
		ctx.lineTo(x + bar_width, y);
		ctx.lineTo(x + bar_width, -y);
		ctx.fill();
	}
	
	// Volume
	volume_height += 4;
	ctx.fillStyle="#404040";
	ctx.beginPath();
	ctx.arc(-volume_width / 2, volume_y, volume_height / 2, 0.5 * Math.PI, 1.5 * Math.PI);
	ctx.lineTo(volume_width / 2, volume_y - volume_height / 2);
	ctx.arc(volume_width / 2, volume_y, volume_height / 2, 1.5 * Math.PI, 0.5 * Math.PI);
	ctx.lineTo(-volume_width / 2, volume_y + volume_height / 2);
	ctx.fill();
	
	volume_height -= 6;
	var temp = volume * volume_width / 30 - volume_width / 2;
	ctx.fillStyle="#2eb82e";
	ctx.beginPath();
	ctx.arc(-volume_width / 2, volume_y, volume_height / 2, 0.5 * Math.PI, 1.5 * Math.PI);
	ctx.lineTo(temp, volume_y - volume_height / 2);
	ctx.arc(temp, volume_y, volume_height / 2, 1.5 * Math.PI, 0.5 * Math.PI);
	ctx.lineTo(-volume_width / 2, volume_y + volume_height / 2);
	ctx.fill();
	volume_height += 2;
	ctx.fillStyle="white";
	ctx.beginPath();
	ctx.arc(temp, volume_y, volume_height*2, 0, 2 * Math.PI);
	ctx.fill();
	
}
function mouse_down()
{
	if(ws == null)
		return;
	
	event.preventDefault();
	
	var x, y;
	
	if(event.offsetX)
	{
		x = event.offsetX - trans_x;
		y = event.offsetY - trans_y;
	}
	else if(event.layerX)
	{
		x = event.layerX - trans_x;
		y = event.layerY - trans_y;
	}
	else
	{
		x = (Math.round(event.touches[0].pageX - event.touches[0].target.offsetLeft)) - trans_x;
		y = (Math.round(event.touches[0].pageY - event.touches[0].target.offsetTop)) - trans_y;
	}
	
	var radius = Math.sqrt(x*x + y*y);
	if(radius < inner_radius)
	{
		// pause/play button is click
		play_state = (play_state + 1)%2;
		
		if(play_state)
			ws.send("P" + "0\r\n");
		else
			ws.send("S" + "0\r\n");
	}
	else if((y < next_top) && (y > -next_top))
	{
		if((x < next_right) && (x > (next_right-next_width)))
		{
			ws.send("N" + "0\r\n");
			console.log("next button is pressed!");
			click_state = 2;
		}
		else if((x > -next_right) && (x < -(next_right-next_width)))
		{
			ws.send("B" + "0\r\n");
			console.log("back button is pressed!");
			click_state = 1;
		}
	}
	else 
	{
		if((y > (volume_y - volume_height*3)) 
			&& (y < (volume_y + volume_height*3))
			&& (x < (volume_width / 2)) 
			&& (x > -(volume_width / 2)))
			{
				volume = Math.round((volume_width / 2 + x)/ volume_width *30);
				ws.send("V" + volume.toString() + "\r\n");
				console.log("volume changed!");
			}
	}
	
	update_view();
}
function mouse_up()
{
	if(ws == null)
		return;
	
	event.preventDefault();
	
	click_state = 0;
	
	update_view();
}
function mouse_move()
{
	if(ws == null)
		return;
 
	event.preventDefault();
	
	var x, y;
	
	if(event.offsetX)
	{
		x = event.offsetX - trans_x;
		y = event.offsetY - trans_y;
	}
	else if(event.layerX)
	{
		x = event.layerX - trans_x;
		y = event.layerY - trans_y;
	}
	else
	{
		x = (Math.round(event.touches[0].pageX - event.touches[0].target.offsetLeft)) - trans_x;
		y = (Math.round(event.touches[0].pageY - event.touches[0].target.offsetTop)) - trans_y;
	}
	
	if((y > (volume_y - volume_height*3)) 
	&& (y < (volume_y + volume_height*3))
	&& (x < (volume_width / 2)) 
	&& (x > -(volume_width / 2)))
	{
		volume = Math.round((volume_width / 2 + x)/ volume_width *30);
		ws.send("V" + volume.toString() + "\r\n");
		console.log("volume changed!");
	}
	
	update_view();
}
window.onload = init;
</script>
</head>

<body>

<p>
<h1>Web Remote Control / IoT MP3 Player</h1>
</p>

<canvas id="remote"></canvas>

<h2>
<p>
WebSocket : <span id="ws_state">null</span>
</p>
<button id="bt_connect" type="button" onclick="connect_onclick();">Connect</button>
</h2>

</body>
</html>

Main task (task0.php)

PHP
<?php

if(_SERVER("REQUEST_METHOD"))
	exit; // avoid php execution via http request

include "/lib/sd_340.php";
include "/lib/sn_tcp_ws.php";

define("CMD_PLAY_NEXT",		0x01);
define("CMD_PLAY_PREV",		0x02);
define("CMD_PLAY_W_INDEX",	0x03);
define("CMD_SET_VOLUME",	0x06);
define("CMD_SEL_DEV",		0x09);
define("CMD_PLAY_W_VOL",	0x22);
define("CMD_PLAY",			0x0D);
define("CMD_PAUSE",			0x0E);
define("CMD_SINGLE_CYCLE",	0x19);
define("DEV_TF",			0x02);
define("SINGLE_CYCLE_ON",	0x00);
define("SINGLE_CYCLE_OFF",	0x01);

function mp3_send_cmd($cmd, $data)
{
    $cmd_buf  = "\x7E"; /* $S - Every command should start with $(0x7E) */
    $cmd_buf .= "\xFF"; /* VER - Version information */
    $cmd_buf .= "\x06"; /* Len - The number of bytes of the command without starting byte and ending byte */
    $cmd_buf .= int2bin($cmd, 1);   /* CMD - Such as PLAY and PAUSE and so on */
    $cmd_buf .= "\x00"; /* Feedback - 0x00 = not feedback, 0x01 = feedback */
    $cmd_buf .= int2bin($data, 2, TRUE);  /* data - usually it has two bytes */
    $cmd_buf .= "\xEF"; /* $O - Ending byte of the command */
	
	uart_write(0, $cmd_buf, 8);
}

uart_setup(0, 9600, "N81N");
ws_setup(0, "remote_slide", "text.phpoc");

$cmd_buf = "";
$rbuf = "";

while(1)
{
	if(ws_state(0) == TCP_CONNECTED)
	{
		$rlen = ws_read_line(0, $rbuf);
		
		if($rlen)
		{
			$name = $rbuf[0];
			$value = (int)substr($rbuf, 1);
			
			if($name == 'P')
			{
				 echo "Play mp3\r\n";
				 mp3_send_cmd(CMD_PLAY, 0x0000);
			}
			
			if($name == 'S')
			{
				 echo "Pause mp3\r\n";
				 mp3_send_cmd(CMD_PAUSE, 0x0000);
			}
			
			if($name == 'N')
			{
				 echo "Play next mp3\r\n";
				 mp3_send_cmd(CMD_PLAY_NEXT, 0x0000);
			}
			if($name == 'B')
			{
				 echo "Play previous mp3\r\n";
				 mp3_send_cmd(CMD_PLAY_PREV, 0x0000);
			}
			if($name == 'V')
			{
				 echo "Change volume to $value\r\n";
				 mp3_send_cmd(CMD_SET_VOLUME, $value);
			}
		}
	}
}

?>

Credits

khanhhs
0 projects • 56 followers
Contact

Comments

Please log in or sign up to comment.