Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Arduino Web-gestuurde XY-plotter met stappenmotorcontroller

Onderdelen en benodigdheden

Arduino UNO 1
PHPoC WiFi Shield 2 voor Arduino 1
PHPoC Stappenmotorcontroller 2
Makeblock XY-plotter 1

Over dit project

Demonstratie

Als je nieuw bent met Arduino, kun je beginnen met:

  • Arduino-tutorials voor beginners
  • Alles over motoren voor Arduino

Hoe het werkt

Wanneer een vinger het tekengebied op de webpagina aanraakt, worden de XY-coördinaten van het aanraakpunt naar de Arduino gestuurd. Na schaling van de coördinaten stuurt de Arduino twee stappenmotoren om de pen op die coördinaat te positioneren. Tijdens de beweging stuurt de Arduino continu de baan van de pen naar de web-app, die de baan tekent op het canvas.

Broncode

De broncode bestaat uit twee delen: Arduino-code en Web User Interface-code.

Arduino-code

Deze Arduino-code draait in een oneindige lus om:

  • Commando's van de webpagina te ontvangen en taken uit te voeren volgens het commando
  • CMD_MOVE: pen naar een positie verplaatsen door twee stappenmotoren te besturen
  • CMD_PEN_UP: pen tillen door de hoek van de servomotor te veranderen
  • CMD_PEN_DOWN: pen neerzetten door de hoek van de servomotor te veranderen
  • Continu de huidige positie van de pen uitlezen en naar de webpagina sturen

Web User Interface

Zie de code in de Codesectie.

  • Gebruikersinterface aanbieden
  • Gebruikersevents afhandelen en commando's met coördinaten naar Arduino sturen
  • Baan van de pen van Arduino ontvangen en op de webpagina tekenen

Het bestand remote_draw.php bevat de Web User Interface. Dit bestand moet opgeslagen worden op de PHPoC [WiFi] Shield. Om het bestand te uploaden naar de shield, volg deze stappen:

  1. Kopieer de onderstaande code en bewaar het als remote_draw.php.
  2. Installeer PHPoC Debugger.
  3. Verbind de PHPoC met de PHPoC [WiFi] Shield via een micro-USB-kabel volgens deze instructie. Let op: de Arduino moet gevoed worden.
  4. Upload remote_draw.php naar de PHPoC [WiFi] Shield volgens deze instructie.

Hoe te gebruiken

  1. Netwerkinformatie configureren voor de PHPoC shield of PHPoC WiFi shield
  2. PHPoC Library installeren
  3. PHPoC Expansion Library installeren
  4. Code compileren en naar Arduino uploaden
  5. Web User Interface naar PHPoC [WiFi] shield uploaden
  6. Seriële Monitor openen en IP-adres van PHPoC Shield kopiëren
  7. Web User Interface openen via webbrowser: https://ip_adres_van_shield/remote_draw.php
  8. Via het web tekenen

Om elektronische componenten te kopen, kun je ze bestellen bij utsource.net.

De beste Arduino Starter Kit voor beginners

Bekijk de beste Arduino Kit voor beginners.

Functiereferenties

  • Serial.begin()
  • Serial.println()
  • delay()
  • millis()
  • for-lus
  • while-lus
  • if-else
  • loop()
  • setup()
  • String.toInt()
  • String.substring()
  • String.indexOf()
  • String.remove()
  • String.equals()

Code

Arduino-code

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

#define MAX_X 55550 // eenheid is stap
#define MAX_Y 68780 // eenheid is stap
#define TOUCH_OFFSET 5000 // eenheid is stap
#define PEN_STATE_UP 0
#define PEN_STATE_DOWN 1
#define CMD_PEN_UP 0
#define CMD_PEN_DOWN 1
#define CMD_MOVE 2
#define STEP_MODE 32
#define SPEED_X_COEF ((long)40 * STEP_MODE)
#define SPEED_Y_COEF ((long)40 * STEP_MODE)
#define SPEED_X_MAX ((long)1500 * STEP_MODE)
#define SPEED_Y_MAX ((long)1500 * STEP_MODE)
#define ACCEL_X_MAX ((long)6000 * STEP_MODE)
#define ACCEL_Y_MAX ((long)6000 * STEP_MODE)
#define STEP_STATE_STOP 0
#define STEP_STATE_LOCK 1
#define RESOLUTION 500
#define MIN_UPDATE_INTERVAL 100 // in milliseconde

PhpocServer server(80);
ExpansionStepper stepX(14);
ExpansionStepper stepY(13);
Servo servo;

long preX = 0;
long preY = 0;
byte penState = PEN_STATE_UP;
bool isUnlockedX = false;
bool isUnlockedY = false;
int forwardDirX = -1; // richting van XY-plotter wanneer motor X vooruit gaat, afhankelijk van installatie
int forwardDirY = +1; // richting van XY-plotter wanneer motor Y omhoog gaat, afhankelijk van installatie
unsigned long lastUpdateMillis;

void penUp() {
  servo.write(110);
  penState = PEN_STATE_UP;
}

void penDown() {
  servo.write(180);
  penState = PEN_STATE_DOWN;
}

void xyWait() {
  while(stepX.getState() > 1 || stepY.getState() > 1) ;
}

void xyInit() {
  penUp();
  stepX.setMode(STEP_MODE);
  stepX.setVrefStop(4);
  stepX.setVrefDrive(15);
  stepX.setResonance(120, 250);
  stepX.setSpeed(20000);
  stepX.setAccel(50000);
  stepY.setMode(STEP_MODE);
  stepY.setVrefStop(4);
  stepY.setVrefDrive(15);
  stepY.setResonance(120, 250);
  stepY.setSpeed(20000);
  stepY.setAccel(50000);

  // pen naar (0, 0) verplaatsen
  stepX.stepGotoSW(0, -forwardDirX);
  stepY.stepGotoSW(0, -forwardDirY);
  xyWait();
  stepX.setPosition(0);
  stepY.setPosition(0);

  // commentaar dit blok uit voor de eerste run en wijzig de waarden in regel 6, 7 van dit bestand en regel 33 van index.php volgens de waarde in IDE console
  /*
  // max stappen controleren
  stepX.stepGotoSW(1, forwardDirX);
  stepY.stepGotoSW(1, forwardDirY);
  xyWait();
  // verander deze waarden in regel 45 van index.php
  Serial.print(F("MAX_X:"));
  Serial.println(stepX.getPosition() * forwardDirX);
  Serial.print(F("MAX_Y:"));
  Serial.println(stepY.getPosition() * forwardDirY);
  */

  xyGoto(TOUCH_OFFSET, TOUCH_OFFSET);
  xyWait();
  stepX.setEioMode(0, EIO_MODE_LOCK);
  stepX.setEioMode(1, EIO_MODE_LOCK);
  stepY.setEioMode(0, EIO_MODE_LOCK);
  stepY.setEioMode(1, EIO_MODE_LOCK);
}

void xyGoto(long x, long y) {
  if(x < TOUCH_OFFSET) x = TOUCH_OFFSET;
  else if(x > (MAX_X - TOUCH_OFFSET)) x = MAX_X - TOUCH_OFFSET;
  if(y < TOUCH_OFFSET) y = TOUCH_OFFSET;
  else if(y > (MAX_Y - TOUCH_OFFSET)) y = MAX_Y - TOUCH_OFFSET;

  x *= forwardDirX;
  y *= forwardDirY;

  int32_t deltaX = x - stepX.getPosition();
  int32_t deltaY = y - stepY.getPosition();
  deltaX = abs(deltaX);
  deltaY = abs(deltaY);

  long speedX = SPEED_X_COEF * abs(deltaX);
  long speedY = SPEED_Y_COEF * abs(deltaY);

  if(speedX > speedY) {
    if(speedX > SPEED_X_MAX) speedX = SPEED_X_MAX;
    double ratio = deltaY / (double)deltaX;
    speedY = (long) (ratio * speedX);
  } else {
    if(speedY > SPEED_Y_MAX) speedY = SPEED_Y_MAX;
    double ratio = deltaX / (double)deltaY;
    speedX = (long) (ratio * speedY);
  }

  long accelX, accelY;
  if(speedX < speedY) {
    accelY = ACCEL_Y_MAX;
    double ratio = accelY / (double)speedY;
    accelX = (long) (ratio * speedX);
  } else {
    accelX = ACCEL_X_MAX;
    double ratio = accelX / (double)speedX;
    accelY = (long) (ratio * speedY);
  }

  if(deltaX != 0) stepX.command(F("goto %ld %lu %lu"), x, speedX, accelX);
  if(deltaY != 0) stepY.command(F("goto %ld %lu %lu"), y, speedY, accelY);
}

void xyCheckUpdateToWeb() {
  bool isUpdate = false;
  unsigned long curMillis = millis();
  if((curMillis - lastUpdateMillis) > MIN_UPDATE_INTERVAL) isUpdate = true;

  long curX = stepX.getPosition();
  long curY = stepY.getPosition();
  long deltaX = curX - preX;
  long deltaY = curY - preY;
  long dist = sqrt(pow(deltaX, 2) + pow(deltaY, 2));

  if(dist > RESOLUTION) isUpdate = true;
  if(isUpdate == false || dist == 0) return false;

  lastUpdateMillis = curMillis;
  preX = curX;
  preY = curY;
  sendPositionToWeb(); // huidige positie naar web sturen
}

void sendPositionToWeb() {
  char wbuf[20];
  long x = stepX.getPosition() * forwardDirX;
  long y = stepY.getPosition() * forwardDirY;
  String data = String(F("[")) + x + String(F(",")) + y + String(F(",")) + penState + String(F("]\n"));
  data.toCharArray(wbuf, data.length() + 1);
  server.write(wbuf, data.length());
}

void setup() {
  Serial.begin(9600);
  while(!Serial) ;
  Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
  server.beginWebSocket("xy_plotter");
  Serial.print("WebSocket server adres: ");
  Serial.println(Phpoc.localIP());
  Expansion.begin(460800);
  servo.attach(8);

  /**
   * OPMERKING: Voor de eerste run:
   * - commentaar het laatste blok in xyInit() uit
   * - Arduino-code uitvoeren
   * - waarden in regel 6, 7 van dit bestand en regel 33 van index.php wijzigen volgens de waarde in IDE console
   */
  xyInit();
  lastUpdateMillis = millis();
}

void loop() {
  // wachten op nieuwe client
  PhpocClient client = server.available();
  if(client) {
    String data = client.readLine();
    if(data) {
      byte separatorPos1 = data.indexOf(':');
      byte separatorPos2 = data.lastIndexOf(':');
      byte cmd = data.substring(0, separatorPos1).toInt();
      long x = data.substring(separatorPos1 + 1, separatorPos2).toInt();
      long y = data.substring(separatorPos2 + 1).toInt();

      switch(cmd) {
        case CMD_PEN_DOWN:
          xyGoto(x, y);
          while(stepX.getState() > 1 || stepY.getState() > 1) xyCheckUpdateToWeb();
          penDown();
          break;
        case CMD_PEN_UP:
          while(stepX.getState() > 1 || stepY.getState() > 1) xyCheckUpdateToWeb();
          penUp();
          break;
        case CMD_MOVE:
          xyGoto(x, y);
          break;
      }
    }
  }
  xyCheckUpdateToWeb();
}

Web User Interface

<!DOCTYPE html>
<html>
<head>
<title>Arduino - PHPoC Shield - XY Plotter</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<style>
body { text-align: center; background-color: #33C7F2; }
#canvas { margin-right: auto; margin-left: auto; position: relative; background-color: #FFFFFF; }
canvas { position: absolute; left: 0px; top: 0px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; }
#layer1 { z-index: 2; }
#layer2 { z-index: 1; }
#layer3 { z-index: 0; }
</style>
<script>
var PEN_STATE_UP = 0;
var PEN_STATE_DOWN = 1;
var CMD_PEN_UP = 0;
var CMD_PEN_DOWN = 1;
var CMD_MOVE = 2;
var RESOLUTION = 1000;
var MIN_UPDATE_INTERVAL = 100; // in milliseconde
var MAX_X = 55550, MAX_Y = 68780;
var CANVAS_WIDTH = 0, CANVAS_HEIGHT = 0;
var ws = null;
var canvas = null;
var layer1 = null, layer2 = null, layer3 = null;
var ctx1 = null, ctx2 = null, ctx3 = null;
var touchState = 0;
var touchStepX = 0, touchStepY = 0; // eenheid is stap
var touchPixelX = 0, touchPixelY = 0; // eenheid is pixel
var plotterX = 0, plotterY = 0;
var plotterPenState = PEN_STATE_UP;
var lastUpdateMillis;
var buffer = "";

function init() {
  canvas = document.getElementById("canvas");
  layer1 = document.getElementById("layer1"); // voor tekenen huidige positie, ontvangen van XY-plotter
  layer2 = document.getElementById("layer2"); // voor tekenen baan van XY-plotter, ontvangen van XY-plotter
  layer3 = document.getElementById("layer3"); // voor tekenen huidige aanraakpositie, ingevoerd door gebruiker op scherm
  ctx1 = layer1.getContext("2d");
  ctx2 = layer2.getContext("2d");
  ctx3 = layer3.getContext("2d");
  layer1.addEventListener("touchstart", mouse_down);
  layer1.addEventListener("touchend", mouse_up);
  layer1.addEventListener("touchmove", mouse_move);
  layer1.addEventListener("mousedown", mouse_down);
  layer1.addEventListener("mouseup", mouse_up);
  layer1.addEventListener("mousemove", mouse_move);
  canvasResize();
  var d = new Date();
  lastUpdateMillis = d.getTime();
}

function ws_onmessage(e_msg) {
  buffer += e_msg.data;
  var pos = buffer.indexOf('\n');
  if(pos == -1) return;
  var data = buffer.substring(0, pos);
  buffer = buffer.substring(pos + 1);
  var arr = JSON.parse(data);
  plotterX = arr[0];
  plotterY = arr[1];
  var newPlotterPenState = arr[2];

  // stap-eenheid omzetten naar pixel-eenheid
  plotterX = Math.round(plotterX * CANVAS_WIDTH / MAX_X);
  plotterY = -Math.round(plotterY * CANVAS_HEIGHT / MAX_Y);

  // huidige positie van plotter tekenen
  ctx1.clearRect(0, 0, CANVAS_WIDTH, -CANVAS_HEIGHT);
  ctx1.beginPath();
  ctx1.arc(plotterX, plotterY, 7, 0, 2*Math.PI);
  ctx1.fill();

  // baan van plotter tekenen alleen als pen neer is
  if(plotterPenState == PEN_STATE_UP && newPlotterPenState == PEN_STATE_DOWN) ctx2.beginPath();
  if(newPlotterPenState == PEN_STATE_DOWN) {
    ctx2.lineTo(plotterX, plotterY);
    ctx2.stroke();
  }
  plotterPenState = newPlotterPenState;
}

function ws_onopen() {
  document.getElementById("ws_state").innerHTML = "OPEN";
  document.getElementById("wc_conn").innerHTML = "Disconnect";
}

function ws_onclose() {
  document.getElementById("ws_state").innerHTML = "CLOSED";
  document.getElementById("wc_conn").innerHTML = "Connect";
  ws.onopen = null;
  ws.onclose = null;
  ws.onmessage = null;
  ws = null;
}

function wc_onclick() {
  if(ws == null) {
    ws = new WebSocket("ws://<?echo _SERVER(\"HTTP_HOST\")?>/xy_plotter", "text.phpoc");
    document.getElementById("ws_state").innerHTML = "CONNECTING";
    ws.onopen = ws_onopen;
    ws.onclose = ws_onclose;
    ws.onmessage = ws_onmessage;
  } else ws.close();
}

function event_handler(event, type) {
  // coördinaat omzetten
  if(event.targetTouches) {
    if(event.targetTouches.length > 1) return false;
    touchPixelX = event.targetTouches[0].pageX - canvas.offsetLeft;
    touchPixelY = event.targetTouches[0].pageY - canvas.offsetTop - CANVAS_HEIGHT;
  } else {
    touchPixelX = event.offsetX;
    touchPixelY = event.offsetY - CANVAS_HEIGHT;
  }

  // van pixel naar stap omzetten
  var newTouchStepX = Math.round(touchPixelX / CANVAS_WIDTH * MAX_X);
  var newTouchStepY = Math.round((-touchPixelY) / CANVAS_HEIGHT * MAX_Y);

  if(type == "MOVE") {
    // updateconditie controleren om te veel data naar plotter te sturen te voorkomen
    var isUpdate = false;
    var d = new Date();
    var curMillis = d.getTime();
    if((curMillis - lastUpdateMillis) > MIN_UPDATE_INTERVAL) {
      isUpdate = true;
    }
    var deltaX = newTouchStepX - touchStepX;
    var deltaY = newTouchStepY - touchStepY;
    var dist = Math.sqrt( Math.pow(deltaX, 2) + Math.pow(deltaY, 2) );
    if(dist > RESOLUTION) isUpdate = true;
    if(isUpdate == false) return false;
    lastUpdateMillis = curMillis;
  }

  touchStepX = newTouchStepX;
  touchStepY = newTouchStepY;
  drawXYline();
  return true;
}

function mouse_down() {
  event.preventDefault();
  event_handler(event, "DOWN");
  sendToPlotter(CMD_PEN_DOWN, touchStepX, touchStepY);
  touchState = 1;
}

function mouse_up() {
  event.preventDefault();
  touchState = 0;
  sendToPlotter(CMD_PEN_UP, touchStepX, touchStepY);
}

function mouse_move() {
  event.preventDefault();
  if(touchState == 1) {
    if(event_handler(event, "MOVE")) sendToPlotter(CMD_MOVE, touchStepX, touchStepY);
  }
}

function sendToPlotter(cmd, x, y) {
  if(ws != null && ws.readyState == 1) ws.send(cmd + ":" + touchStepX + ":" + touchStepY + "\r\n");
}

function canvasClear() {
  ctx2.clearRect(0, 0, CANVAS_WIDTH, -CANVAS_HEIGHT); // alleen baan wissen
}

function canvasResize() {
  var width = Math.round(window.innerWidth*0.95);
  var height = Math.round(window.innerHeight*0.95) - 100;
  var temp_height = Math.round(width*MAX_Y/MAX_X);
  if(temp_height <= height) {
    CANVAS_WIDTH = width;
    CANVAS_HEIGHT = temp_height;
  } else {
    CANVAS_WIDTH = height*MAX_X/MAX_Y;
    CANVAS_HEIGHT = height;
  }
  canvas.style.width = CANVAS_WIDTH + "px";
  canvas.style.height = CANVAS_HEIGHT + "px";
  layer1.width = CANVAS_WIDTH;
  layer1.height = CANVAS_HEIGHT;
  layer2.width = CANVAS_WIDTH;
  layer2.height = CANVAS_HEIGHT;
  layer3.width = CANVAS_WIDTH;
  layer3.height = CANVAS_HEIGHT;
  ctx1.translate(0, CANVAS_HEIGHT);
  ctx2.translate(0, CANVAS_HEIGHT);
  ctx3.translate(0, CANVAS_HEIGHT);
  ctx1.fillStyle = "#00979d";
  ctx2.lineWidth = 3;
  ctx2.strokeStyle = "black";
  ctx3.strokeStyle = "00FFFF";
}

function drawXYline() {
  ctx3.clearRect(0, 0, CANVAS_WIDTH, -CANVAS_HEIGHT);
  ctx3.beginPath();
  ctx3.moveTo(0, touchPixelY);
  ctx3.lineTo(CANVAS_WIDTH, touchPixelY);
  ctx3.moveTo(touchPixelX, 0);
  ctx3.lineTo(touchPixelX, -CANVAS_HEIGHT);
  ctx3.stroke();
}

window.onload = init;
</script>
</head>
<body onresize="canvasResize()">
<br>
<div id="canvas">
  <canvas id="layer1"></canvas>
  <canvas id="layer2"></canvas>
  <canvas id="layer3"></canvas>
</div>
<p>WebSocket : <span id="ws_state">null</span></p>
<button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button>
<button type="button" onclick="canvasClear();">Clear</button>
</body>
</html>

Productieproces

  1. Een DTMF-decoder bouwen met alleen een Arduino – Geen extra IC's nodig
  2. Web-gestuurd DMX-verlichtingssysteem – Arduino Master Controller
  3. Arduino Uno Gebaseerde Mensendetectie Robot: Stap-voor-stap Sensorintegratie
  4. DIY Arduino USB Gaming Controller – Bouw Je Eigen High-Performance Gamepad
  5. SERENA: Een Aangepast Arduino Mega 2560 Alarmsysteem Met TFT LCD Touchscreen
  6. Bouw een Android-bestuurd extern voertuig met Raspberry Pi Motor Shield
  7. Bouw Een Autonome Lijnvolgende Robot Met Arduino UNO
  8. Bestuur een Arduino-rover naadloos met Firmata en een Xbox One-controller
  9. Arduino UNO & WiFi Shield: Realtime Temperatuurweergave op het Web via Serieel
  10. Beveilig uw Arduino met Webpatroonontgrendeling – Eenvoudige Setup & Remote Control
  11. Uitgebreide Arduino Trainingskit met Mega 2560, Sensoren, Motoren en Custom PCB