Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Plotter XY Controlado via Web com Arduino e Motores de Passo

Componentes e Materiais

Arduino UNO × 1
PHPoC WiFi Shield 2 para Arduino × 1
Controlador de Motor de Passo PHPoC × 2
Plotter XY Makeblock × 1

Sobre o Projeto

Demonstração

Se você é novo no Arduino, pode começar com:

Como Funciona

Quando o usuário toca na área de desenho na página web, as coordenadas XY do ponto de toque são enviadas ao Arduino. Após escalar as coordenadas, o Arduino move dois motores de passo para posicionar a caneta naquela coordenada. Durante o movimento, o Arduino envia continuamente a trajetória da caneta para a aplicação web, que desenha a trajetória no canvas.

Código Fonte

O código fonte inclui duas partes: código Arduino e código da Interface Web do Usuário.

Código Arduino

Este é o código Arduino, que roda em loop infinito para:

Interface Web do Usuário

Veja o código na seção Código.

O arquivo remote_draw.php contém a Interface Web do Usuário. Ele precisa ser armazenado no PHPoC [WiFi] Shield. Para fazer upload do arquivo para o PHPoC [WiFi] Shield, siga estes passos:

  1. Copie o código abaixo e salve como remote_draw.php.
  2. Instale o PHPoC Debugger.
  3. Conecte o PHPoC ao PHPoC [WiFi] Shield via cabo micro-USB conforme esta instrução. O Arduino deve estar alimentado.
  4. Faça upload do arquivo remote_draw.php para o PHPoC [WiFi] Shield conforme esta instrução.

Como Executar

  1. Configure as informações de rede para o PHPoC shield ou PHPoC WiFi shield.
  2. Instale a Biblioteca PHPoC.
  3. Instale a Biblioteca de Expansão PHPoC.
  4. Compile e faça upload do código para o Arduino.
  5. Faça upload da interface web para o PHPoC [WiFi] shield.
  6. Abra o Monitor Serial e copie o endereço IP do PHPoC Shield.
  7. Acesse a Interface Web via Navegador: https://ip_address_of_shield/remote_draw.php
  8. Desenhe via Web.

Para comprar componentes eletrônicos, você pode encomendá-los em utsource.net.

O Melhor Kit Inicial Arduino para Iniciantes

Veja O Melhor Kit Arduino para Iniciantes.

Referências de Funções

Código

Código Arduino

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

#define MAX_X 55550 // unidade: passo
#define MAX_Y 68780 // unidade: passo
#define TOUCH_OFFSET 5000 // unidade: passo
#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 // em milissegundo

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; // direção do plotter XY quando motor X move para frente, depende da instalação, deve ser testado para determinar os valores
int forwardDirY = +1; // direção do plotter XY quando motor Y move para cima, depende da instalação, deve ser testado para determinar os valores
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);

  // mover caneta para (0, 0)
  stepX.stepGotoSW(0, -forwardDirX);
  stepY.stepGotoSW(0, -forwardDirY);
  xyWait();
  stepX.setPosition(0);
  stepY.setPosition(0);

  // descomente este bloco para a primeira execução e altere os valores nas linhas 6, 7 deste arquivo e linha 33 do index.php de acordo com o valor no console da IDE
  /*
  // verificar passos máximos
  stepX.stepGotoSW(1, forwardDirX);
  stepY.stepGotoSW(1, forwardDirY);
  xyWait();
  // altere estes valores na linha 45 do 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;
  long 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(); // enviar posição atual para exibir na web
}

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("Endereço do servidor WebSocket: ");
  Serial.println(Phpoc.localIP());
  Expansion.begin(460800);
  servo.attach(8);

  /**
   * NOTA: Para a primeira execução:
   * - descomente o último bloco em xyInit()
   * - execute o código Arduino
   * - altere os valores nas linhas 6, 7 deste arquivo e linha 33 do index.php de acordo com o valor no console da IDE.
   **/
  xyInit();
  lastUpdateMillis = millis();
}

void loop() {
  // aguardar novo cliente:
  PhpocClient client = server.available();
  if(client) {
    String data = client.readLine();
    if(data) {
      //Serial.println(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);
          //xyWait();
          while(stepX.getState() > 1 || stepY.getState() > 1) xyCheckUpdateToWeb();
          penDown();
          break;
        case CMD_PEN_UP:
          //xyWait();
          while(stepX.getState() > 1 || stepY.getState() > 1) xyCheckUpdateToWeb();
          penUp();
          break;
        case CMD_MOVE:
          xyGoto(x, y);
          break;
      }
    }
  }
  xyCheckUpdateToWeb();
}

Interface Web do Usuário (PHP)

<!DOCTYPE html>
<html>
<head>
<title>Arduino - PHPoC Shield - Plotter XY</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; // em milissegundo
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; /* unidade: passo */
var touchPixelX = 0, touchPixelY = 0; /* unidade: 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"); /* para desenhar posição atual, recebida do plotter XY */
  layer2 = document.getElementById("layer2"); /* para desenhar trajetória do plotter XY, recebida do plotter XY */
  layer3 = document.getElementById("layer3"); /* para desenhar posição atual do toque, inserida pelo usuário na tela */
  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];
  /* converter unidade de passo para unidade de pixel */
  plotterX = Math.round(plotterX * CANVAS_WIDTH / MAX_X);
  plotterY = -Math.round(plotterY * CANVAS_HEIGHT / MAX_Y);
  /* desenhar posição atual do plotter */
  ctx1.clearRect(0, 0, CANVAS_WIDTH, -CANVAS_HEIGHT);
  ctx1.beginPath();
  ctx1.arc(plotterX, plotterY, 7, 0, 2*Math.PI);
  ctx1.fill();
  /* desenhar trajetória do plotter apenas quando caneta baixada */
  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) {
  // converter coordenada
  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;
  }
  /* converter de pixel para passo */
  var newTouchStepX = Math.round(touchPixelX / CANVAS_WIDTH * MAX_X);
  var newTouchStepY = Math.round((-touchPixelY) / CANVAS_HEIGHT * MAX_Y);

  if(type == "MOVE") {
    /* verificar condição de atualização para evitar envio excessivo de dados ao plotter */
    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); /* limpar apenas trajetória */
}

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>

Processo de manufatura

  1. Construa um Decodificador DTMF Usando Apenas um Arduino – Sem CIs Externos
  2. Sistema de Iluminação DMX Controlado via Web – Controlador Mestre Arduino
  3. Robô de Detecção Humana Baseado em Arduino Uno: Integração de Sensores Passo a Passo
  4. Controle USB para Jogos DIY com Arduino – Construa Seu Próprio Gamepad de Alto Desempenho
  5. SERENA: Sistema de Alarme Personalizado Arduino Mega 2560 com Touchscreen TFT LCD
  6. Veículo Remoto Controlado por Android com Raspberry Pi e Motor Shield
  7. Criar Robô Seguidor de Linha Autônomo com Arduino UNO
  8. Controle um Rover Arduino com Firmata e Gamepad Xbox One
  9. Arduino UNO com Shield WiFi: Exibição de Temperatura em Tempo Real na Web via Serial
  10. Proteja seu Arduino com Desbloqueio por Padrão Web – Configuração Fácil e Controle Remoto