Tutorial Video
Hardware
- PHPoC Blue (+ USB WLAN) or PHPoC Black (+ Ethernet cable)
- Micro USB to USB Cable (to upload source code to PHPoC Device)
- Servo Motor
- Jumper wires
About Servo Motor
Stardard servo motor is used to control of angular (usually between 0 and 180 degrees). The servo motor used in this example includes three wires:
- VCC wire.
- GND wire.
- Signal wire (receives the PWM control signal from controller).
- If generating PWM signal with minimum duty cycle to signal wire, the servo motor rotate to 0 degree.
- If generating PWM signal with maximun duty cycle to signal wire, the servo motor rotate to 180 degree.
- If generating PWM signal with duty cycle between minimum and maximum value to signal wire, the servo motor rotate to position that is proportional to duty cycle.
- Harware timer and software timer on PHPoC device can generate PWM signal. Therefore, It just needs connect the HT pin (or selectable IO pin if using software timer) of PHPoC device to the signal wire of servo motor. And then use hardware/software timer to control servo motor.
Wiring Diagram

Quick Steps
Source code of this example is a part of PHPoC Support Packet (PSP). You need to:
- Download PHPoC Support Package.
- Upload example\p4s\05.html5_graphics\03.ht_pwm_servo to PHPoC Blue/Black.
- Configure network parameters (e.g. WiFi SSID, password, IP address ...).
- Click "Run" button on PHPoC Debugger.
- Access webpage on PHPoC using Web Browser on your PC or smart phone (See How To).
Source Code
Source files includes:
- init.php: this file is run when PHPoC system is powered or reset. It is used to specify which file is run is system loop.
- task0.php: this file is run in system loop of PHPoC devices. It acts as WebSocket server and also interacts with servo motor.
- index.php: this file contains source code of web page.It is only run in response to request from Web Browser. It contains webpage (user interface) and acts WebSocket client.
- servo_body.png: Image.
- servo_bracket.png: Image.
index.php
[Full Code]
PHP Code:
<!DOCTYPE html>
<html>
<head>
<title>PHPoC / <?echo system("uname -i")?></title>
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<style>
body { text-align: center; }
canvas { background-color: #f0f0f0; }
</style>
<script>
var canvas_width = 401, canvas_height = 466;
var pivot_x = 200, pivot_y = 200;
var bracket_radius = 160, bracket_angle = 0;
var bracket_img = new Image();
var click_state = 0;
var last_angle = 0;
var mouse_xyra = {x:0, y:0, r:0.0, a:0.0};
var ws;
bracket_img.src = "servo_bracket.png";
function init()
{
var servo = document.getElementById("servo");
servo.width = canvas_width;
servo.height = canvas_height;
servo.style.backgroundImage = "url('/servo_body.png')";
servo.addEventListener("touchstart", mouse_down);
servo.addEventListener("touchend", mouse_up);
servo.addEventListener("touchmove", mouse_move);
servo.addEventListener("mousedown", mouse_down);
servo.addEventListener("mouseup", mouse_up);
servo.addEventListener("mousemove", mouse_move);
var ctx = servo.getContext("2d");
ctx.translate(pivot_x, pivot_y);
rotate_bracket(0);
ws = new WebSocket("ws://<?echo _SERVER("HTTP_HOST")?>/ht_pwm_servo", "csv.phpoc");
document.getElementById("ws_state").innerHTML = "CONNECTING";
ws.onopen = function(){ document.getElementById("ws_state").innerHTML = "OPEN" };
ws.onclose = function(){ document.getElementById("ws_state").innerHTML = "CLOSED"};
ws.onerror = function(){ alert("websocket error " + this.url) };
ws.onmessage = ws_onmessage;
}
function ws_onmessage(e_msg)
{
e_msg = e_msg || window.event; // MessageEvent
alert("msg : " + e_msg.data);
}
function rotate_bracket(angle)
{
var servo = document.getElementById("servo");
var ctx = servo.getContext("2d");
ctx.clearRect(-pivot_x, -pivot_y, canvas_width, canvas_height);
ctx.rotate(angle / 180 * Math.PI);
ctx.drawImage(bracket_img, -pivot_x, -pivot_y);
ctx.rotate(-angle / 180 * Math.PI);
}
function check_range_xyra(event, mouse_xyra)
{
var x, y, r, a, rc_x, rc_y, radian;
var min_r, max_r, width;
if(event.touches)
{
var touches = event.touches;
x = (touches[0].pageX - touches[0].target.offsetLeft) - pivot_x;
y = pivot_y - (touches[0].pageY - touches[0].target.offsetTop);
min_r = 60;
max_r = pivot_x;
width = 40;
}
else
{
x = event.offsetX - pivot_x;
y = pivot_y - event.offsetY;
min_r = 60;
max_r = bracket_radius;
width = 20;
}
/* cartesian to polar coordinate conversion */
r = Math.sqrt(x * x + y * y);
a = Math.atan2(y, x);
mouse_xyra.x = x;
mouse_xyra.y = y;
mouse_xyra.r = r;
mouse_xyra.a = a;
radian = bracket_angle / 180 * Math.PI;
/* rotate coordinate */
rc_x = x * Math.cos(radian) - y * Math.sin(radian);
rc_y = x * Math.sin(radian) + y * Math.cos(radian);
if((r < min_r) || (r > max_r))
return false;
if((rc_y < -width) || (rc_y > width))
return false;
return true;
}
function mouse_down()
{
if(event.touches && (event.touches.length > 1))
click_state = event.touches.length;
if(click_state > 1)
return;
if(check_range_xyra(event, mouse_xyra))
{
click_state = 1;
last_angle = mouse_xyra.a / Math.PI * 180.0;
}
}
function mouse_up()
{
click_state = 0;
}
function mouse_move()
{
var angle;
if(event.touches && (event.touches.length > 1))
click_state = event.touches.length;
if(click_state > 1)
return;
if(!click_state)
return;
if(!check_range_xyra(event, mouse_xyra))
{
click_state = 0;
return;
}
angle = mouse_xyra.a / Math.PI * 180.0;
if((Math.abs(angle) > 90) && (angle * last_angle < 0))
{
if(last_angle > 0)
last_angle = -180;
else
last_angle = 180;
}
bracket_angle += (last_angle - angle);
last_angle = angle;
if(bracket_angle > 90)
bracket_angle = 90;
if(bracket_angle < -90)
bracket_angle = -90;
rotate_bracket(bracket_angle);
if(ws.readyState == 1)
ws.send(Math.floor(bracket_angle) + "\r\n");
debug = document.getElementById("debug");
debug.innerHTML = Math.floor(bracket_angle);
event.preventDefault();
}
window.onload = init;
</script>
</head>
<body>
<h2>
HT / Tower Pro SG92R Micro Servo<br>
<br>
<canvas id="servo"></canvas>
<p>
WebSocket : <span id="ws_state">null</span><br>
Angle : <span id="debug">0</span>
</p>
</h2>
</body>
</html>
[Explanation]
Source code of index.php file is composed of HTML, CSS, JavaScript and PHPoC code.
PHPoC code is interpreted on on PHPoC device.
PHPoC code may add/update the content of HTML, CSS or JavaScript code. Once PHPoC code is interpreted in PHPoC, the remaining code is client-side code and it is returned to Web Browser. Web Browser receives this code and interpret it to display the webpage.
- HTML: describes the structure of Web pages
- CSS: describes how HTML elements are to be displayed
- JavaScript: This code: - Handle click/touch event from user on Webpage,
- Calculate angle based on click/touch position,
- Send rotated angle of servo motor to PHPoC device via WebSocket
- Update Graphic UI: Draw arm horn on canvas with respect to the calculated angle
init.php
This file is run when PHPoC system is powered or reset. It is used to specify that task0.php is run is system loop.
PHP Code:
<?php
system("php task0.php");
?>
task0.php
[Full Code]
PHP Code:
<?php
if(_SERVER("REQUEST_METHOD"))
exit; // avoid php execution via http request
include "/lib/sd_340.php";
include "/lib/sn_tcp_ws.php";
define("PWM_PERIOD", 20000); // 20000us (20ms)
define("WIDTH_MIN", 600);
define("WIDTH_MAX", 2450);
ht_pwm_setup(0, (WIDTH_MIN + WIDTH_MAX) / 2, PWM_PERIOD, "us");
ws_setup(0, "ht_pwm_servo", "csv.phpoc");
$rwbuf = "";
while(1)
{
if(ws_state(0) == TCP_CONNECTED)
{
$rlen = ws_read_line(0, $rwbuf);
if($rlen)
{
$angle = -(int)$rwbuf + 90;
if($angle < 0)
$angle = 0;
if($angle > 180)
$angle = 180;
$width = WIDTH_MIN + (int)round((WIDTH_MAX - WIDTH_MIN) * $angle / 180.0);
if(($width >= WIDTH_MIN) && ($width <= WIDTH_MAX))
ht_pwm_width(0, $width, PWM_PERIOD);
}
}
}
?>
[Explanation]
Source code of this file does:
- Setup and initialize HT0 timer to generate PWM signal.
- Setup and initialize WebSocket.
- Receive data (rotated angle )from Web Browser via WebSocket.
- Calculate PWM duty cycle based on the received angle
- Generate PWM signal to control servo motor
See Also
- Servo Motor - How to Control Servo Motor.
- Servo Motor - Controlling Servo Motor from Webpage using Hypertext.
- Servo Motor - Controlling Servo Motor from Webpage with Image.
Other Resources
- PHPoC System Files
- How PHPoC System Works
- How To Use PHPoC Support Package
- WebSocket
- How to Create Embedded Web Apps with WebSocket on PHPoC
- How to Control Devices via Web Browser
- How to Control Devices via HTTP Request
- How to Control Devices via WebSocket
- How to Monitor Sensors/Devices via Web Browser
- How to Monitor Sensors/Devices via HTTP Request
- How to Monitor Sensors/Devices via WebSocket
- How to Access Web Apps on PHPoC