phpoc_man
Published © GPL3+

Controlling Stepper Motor Precisely via Web

This project shows how to control the step motor precisely using micro-stepping method via Web.

BeginnerShowcase (no instructions)2,270
Controlling Stepper Motor Precisely via Web

Things used in this project

Story

Read more

Schematics

Image (step_plate.png)

Code

Main task (task0.php)

PHP
<?php

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

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

define("STEPPER_SID", 14);
define("SPEED_LOW_LIMIT", 32);
define("SPEED_HIGH_LIMIT", 100000);

function step_loop()
{
	$last_tick = st_free_get_count(0);
	$rwbuf = "";

	while(ws_state(0) == TCP_CONNECTED)
	{
		$rlen = ws_read_line(0, $rwbuf);

		if($rlen)
		{
			$dur_ms = st_free_get_count(0) - $last_tick;

			$last_tick = st_free_get_count(0);

			$angle = -(float)$rwbuf;
			$pos = (int)(200.0 * 32.0 * $angle / 360.0);

			$step = $pos - (int)spc_request_dev(STEPPER_SID, "get pos");

			if($step)
			{
				$speed = (int)abs((float)$step / (float)$dur_ms * 1000.0);
				$speed = $speed * 8 / 10;

				if($speed < SPEED_LOW_LIMIT)
					$speed = SPEED_LOW_LIMIT;
				else
				if($speed > SPEED_HIGH_LIMIT)
					$speed = SPEED_HIGH_LIMIT;

				spc_request_dev(STEPPER_SID, "goto $pos $speed 1000k");
				//echo "$step / $dur_ms  $speed\r\n";
			}
		}

		usleep(1000);
	}
}

function ws_loop()
{
	spc_request_dev(STEPPER_SID, "reset");

	ws_setup(0, "spc_stepper_rotate", "csv.phpoc");
	st_free_setup(0, "ms");
	 
	while(1)
	{
		if(ws_state(0) == TCP_CONNECTED)
		{
			$pos = (int)spc_request_dev(STEPPER_SID, "get pos");
			$angle = -(float)$pos / (200.0 * 32.0) * 360.0;

			ws_write(0, (string)$angle . "\r\n");

			step_loop();
		}
	}
}

spc_reset();
spc_sync_baud(460800);

spc_request_dev(STEPPER_SID, "set vref drive 15");
spc_request_dev(STEPPER_SID, "set mode 32");

ws_loop();

?>

User Interface (index.php)

PHP
<!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; background-color: whiite;}
canvas { background-color: white; }
</style>
<script>
var MIN_TOUCH_RADIUS = 20;
var MAX_TOUCH_RADIUS = 200;
var CANVAS_WIDTH = 402, CANVAS_HEIGHT = 402;
var PIVOT_X = 201, PIVOT_Y = 201;
var plate_angle = 0;
var plate_img = new Image();
var click_state = 0;
var last_angle_pos = 0;
var mouse_xyra = {x:0, y:0, r:0.0, a:0.0};
var ws;

plate_img.src = "step_plate.png";

function init()
{
	var stepper = document.getElementById("stepper");

	stepper.width = CANVAS_WIDTH;
	stepper.height = CANVAS_HEIGHT;

	stepper.addEventListener("touchstart", mouse_down);
	stepper.addEventListener("touchend", mouse_up);
	stepper.addEventListener("touchmove", mouse_move);
	stepper.addEventListener("mousedown", mouse_down);
	stepper.addEventListener("mouseup", mouse_up);
	stepper.addEventListener("mousemove", mouse_move);

	var ctx = stepper.getContext("2d");

	ctx.translate(PIVOT_X, PIVOT_Y);

	rotate_plate(0);

	ws = new WebSocket("ws://<?echo _SERVER("HTTP_HOST")?>/spc_stepper_rotate", "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

	plate_angle = Number(e_msg.data);

	rotate_plate(plate_angle);
	//alert("msg : " + e_msg.data);
}
function rotate_plate(angle)
{
	var stepper = document.getElementById("stepper");
	var ctx = stepper.getContext("2d");

	ctx.clearRect(-PIVOT_X, -PIVOT_Y, CANVAS_WIDTH, CANVAS_HEIGHT);
	ctx.rotate(-angle / 180 * Math.PI);

	ctx.drawImage(plate_img, -PIVOT_X, -PIVOT_Y);

	ctx.rotate(angle / 180 * Math.PI);

	debug = document.getElementById("debug");
	debug.innerHTML = plate_angle.toFixed(1);
}
function check_update_xyra(event, mouse_xyra)
{
	var x, y, r, a;
	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);
	}
	else
	{
		x = event.offsetX - PIVOT_X;
		y = PIVOT_Y - event.offsetY;
	}

	/* 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;

	if((r >= MIN_TOUCH_RADIUS) && (r <= MAX_TOUCH_RADIUS))
		return true;
	else
		return false;
}
function mouse_down()
{
	if(event.touches && (event.touches.length > 1))
		click_state = event.touches.length;

	if(click_state > 1)
		return;

	if(check_update_xyra(event, mouse_xyra))
	{
		click_state = 1;
		last_angle_pos = mouse_xyra.a / Math.PI * 180.0;
	}
}
function mouse_up()
{
	click_state = 0;
}
function mouse_move()
{
	var angle_pos, angle_offset;

	if(event.touches && (event.touches.length > 1))
		click_state = event.touches.length;

	if(click_state > 1)
		return;

	if(!click_state)
		return;

	if(!check_update_xyra(event, mouse_xyra))
	{
		click_state = 0;
		return;
	}

	angle_pos = mouse_xyra.a / Math.PI * 180.0;

	if(angle_pos < 0.0)
		angle_pos = angle_pos + 360.0;

	angle_offset = angle_pos - last_angle_pos;
	last_angle_pos = angle_pos;

	if(angle_offset > 180.0)
		angle_offset = -360.0 + angle_offset;
	else
	if(angle_offset < -180.0)
		angle_offset = 360 + angle_offset;

	plate_angle += angle_offset;

	rotate_plate(plate_angle);

	if(ws.readyState == 1)
		ws.send(plate_angle.toFixed(4) + "\r\n");

	event.preventDefault();
}
window.onload = init;
</script>
</head>

<body>

<h2>
Smart Expansion / Stepper Rotate<br>
<br>

<canvas id="stepper"></canvas>

<p>
WebSocket : <span id="ws_state">null</span><br>
Angle : <span id="debug">0</span>
</p>
</h2>

</body>
</html>

Credits

phpoc_man

phpoc_man

62 projects • 406 followers

Comments