Demonstration





Things used in this project




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.

Click image for larger version  Name:	arduino_web_step_hardware.jpg Views:	1 Size:	72.6 KB ID:	1339





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
Click image for larger version  Name:	step_plate.png Views:	1 Size:	11.4 KB ID:	1324

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
    Click image for larger version  Name:	step_motor_web_ui.png Views:	1 Size:	41.7 KB ID:	1325