IoT_lover
Published © GPL3+

Arduino - Control Step Motor Precisely via Web

Controlling a step motor by rotating a plate on web.

IntermediateFull instructions provided20,567

Things used in this project

Story

Read more

Schematics

arduino_web_step_hardware_NyXyk1dlCv.jpg

Code

Arduino Code

Arduino
#include <Phpoc.h>
#include <PhpocExpansion.h>

#define CMD_GET	0
#define CMD_SET	1
#define SPEED_LOW_LIMIT		32
#define SPEED_HIGH_LIMIT	100000
#define MICRO_STEP_MODE	32
#define ANGLE_PER_STEP	1.8 // 1.8 degree per full step
#define ANGLE_PER_MICRO_STEP	(ANGLE_PER_STEP / MICRO_STEP_MODE)

PhpocServer server(80);
ExpansionStepper step(1);

unsigned long previousMillis;

void setup() {
	Serial.begin(9600);
	while(!Serial)
		;

	Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
	server.beginWebSocket("remote_rotate");

	Serial.print("WebSocket server address : ");
	Serial.println(Phpoc.localIP());

	Expansion.begin();

	step.setMode(MICRO_STEP_MODE);
	step.setAccel(1000000);

	previousMillis = millis();
}

void loop() {
	PhpocClient client = server.available();

	if(client) {
		 String data = client.readLine();

		if(data) {
			int commaPos, cmd;
			float angle;

			commaPos = data.indexOf(',');
			cmd = data.substring(0, commaPos).toInt();
			angle = -(data.substring(commaPos + 1).toFloat());

			if(cmd == CMD_GET) {
				// send current angle to web
				char wbuf[10];
				long pos = step.getPosition();
				angle = -(float)pos * ANGLE_PER_MICRO_STEP;

				String angleStr = String(angle);
				
				angleStr.toCharArray(wbuf, angleStr.length() + 1);
				server.write(wbuf, angleStr.length());
			} else if(cmd == CMD_SET) {
				long pos, stepNum;

				pos = (long)(angle / ANGLE_PER_MICRO_STEP);

				stepNum = pos - step.getPosition();

				if(stepNum) {
					unsigned long duration_ms;
					long speed;

					duration_ms = millis() - previousMillis;
					previousMillis = millis();

					speed = (long)abs((double)stepNum / duration_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;

					//step.setSpeed(speed);
					//step.stepGoto(pos);
					step.command(F("goto %ld %ld"), pos, speed);

					Serial.print(F("Go to: "));
					Serial.println(pos);
				}
			}
		}
	}
}

Web User Interface

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 CMD_GET = 0;
var CMD_SET = 1;
var MIN_TOUCH_RADIUS = 20;
var MAX_TOUCH_RADIUS = 200;
var CANVAS_WIDTH = 400, CANVAS_HEIGHT = 400;
var PIVOT_X = 200, PIVOT_Y = 200;
var plate_img = new Image();
var plate_angle = 0;
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")?>/remote_rotate", "text.phpoc");
	document.getElementById("ws_state").innerHTML = "CONNECTING";

	ws.onopen  = ws_onopen;
	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);
}
function ws_onopen() {
	document.getElementById("ws_state").innerHTML = "OPEN" 
	
	setTimeout(function() {
		ws.send(CMD_GET + ",0\r\n");
	}, 50);
}
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, CANVAS_WIDTH, CANVAS_HEIGHT);

	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(CMD_SET + "," + 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

IoT_lover

IoT_lover

10 projects • 190 followers

Comments