Things used in this project
- Arduino Uno or Mega
- PHPoC Shield R2 or PHPoC WiFi Shield R2
- Stepper Motor Controller PES-2605
- A bipolar stepper motor or unipolar stepper motor
- Jumper wires
How It Works
When user access webpage of PHPoC [WiFi] Shield from a web browser on Smart Phone or PC, a WebSocket connection will be created between Arduino and web browser.
WebSocket connection allows to real-time exchange data between web browser and Arduino without reloading webpage.
When user rotates the plate on webpage, the rotated angle will be send to Arduino.
Arduino convert angle to the equivalent number of steps, and then move step motor to the equivalent position.
Angle to the Number of Step Calculation
Assumption:
- Angle per step: ANGLE_PER_STEP. This value is got from motor specification.
- Angle per mirco-step: ANGLE_PER_MICRO_STEP = ANGLE_PER_STEP / MICRO_STEP_MODE
=> the number of micro-step = angle / ANGLE_PER_MICRO_STEP
The motor I used: ANGLE_PER_STEP = 1.8
The mocro-stepping mode I used in this code : MICRO_STEP_MODE = 32
Wiring
- Stack PHPoC Shield or PHPoC WiFi Shield on Arduino
- Stack Stepper Motor Controller PES-2604 on PHPoC Shield or PHPoC WiFi Shield
- Connect stepper motor to terminal block of Stepper Motor Controller PES-2605
- Bipolar stepper motor
- Unipolar stepper motor: there are two ways to connect a unipolar stepper motor to terminal block of PES-2605. User can choose one of them.
- Bipolar stepper motor
Source Code
Arduino Code
This is arduino code, which run in infinite loop.
Code:
#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 - remote_rotate.php
remote_rotate.php is a file that contains Web User Interface. It needs to be stored on PHPoC [WiFi] Shield. In order to upload the file to PHPoC [WiFi] Shield, please do the following steps:
- Copy the below code and save it into remote_rotate.php file.
- Install PHPoC Debugger
- Connect PHPoC to PHPoC [WiFi] Shield via micro-USB cable according to this instruction.
Note that Arduino must be powered. - Upload remote_rotate.php file to PHPoC [WiFi] Shield according to this instruction
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; 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>
The image used in Web User Interface
step_plate.png
The image also needs to be uploaded to PHPoC [WiFi] Shield
How To
- Config network information for PHPoC shield or PHPoC WiFi shield
- Install PHPoC Library
- Install PHPoC Expansion Library
- Compile and upload code to Arduino
- Upload web user interface to PHPoC [WiFi] shield
- Open Serial Monitor and copy IP address of PHPoC Shield
- Access Web User Interface via Web Browser: http://ip_address_of_shield/remote_rotate.php
- Control step motor via Web