Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Traceur XY Piloté par Web avec Arduino et Contrôleur de Moteur Pas à Pas

Composants et fournitures

Arduino UNO × 1
PHPoC WiFi Shield 2 pour Arduino × 1
Contrôleur de Moteur Pas à Pas PHPoC × 2
Traceur XY Makeblock × 1

À propos de ce projet

Démonstration

Si vous débutez avec Arduino, vous pouvez commencer par :

Fonctionnement

Lorsqu'un doigt touche la zone de dessin sur la page web, les coordonnées XY du point de contact sont envoyées à l'Arduino. Après mise à l'échelle des coordonnées, l'Arduino commande les deux moteurs pas à pas pour positionner le stylo à ces coordonnées. Pendant le déplacement, l'Arduino envoie en continu la trajectoire du stylo à l'application web, qui la dessine sur le canvas.

Code source

Le code source comprend deux parties : le code Arduino et le code de l'interface utilisateur web.

Code Arduino

Ce code s'exécute en boucle infinie pour :

Interface Utilisateur Web

remote_draw.php est le fichier contenant l'interface utilisateur web. Il doit être stocké sur le bouclier PHPoC [WiFi]. Pour téléverser le fichier sur le bouclier PHPoC [WiFi], suivez ces étapes :

Procédure

Pour acheter des composants électroniques, vous pouvez les commander sur utsource.net.

Le Meilleur Kit de Démarrage Arduino pour Débutant

Voir Le Meilleur Kit Arduino pour Débutants.

Références de Fonctions

Code

Code Arduino

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

#define MAX_X 55550 // unité : pas
#define MAX_Y 68780 // unité : pas
#define TOUCH_OFFSET 5000 // unité : pas
#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 // en millisecondes

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; /* direction du traceur XY quand le moteur X avance, selon l'installation, à tester pour déterminer les valeurs */
int forwardDirY = +1; /* direction du traceur XY quand le moteur Y monte, selon l'installation, à tester pour déterminer les valeurs */
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);

  // déplacer le stylo vers (0, 0)
  stepX.stepGotoSW(0, -forwardDirX);
  stepY.stepGotoSW(0, -forwardDirY);
  xyWait();
  stepX.setPosition(0);
  stepY.setPosition(0);

  // décommenter ce bloc pour la première exécution et modifier les valeurs ligne 6, 7 de ce fichier et ligne 33 de index.php selon les valeurs de la console IDE
  /*
  // vérifier le nombre max de pas
  stepX.stepGotoSW(1, forwardDirX);
  stepY.stepGotoSW(1, forwardDirY);
  xyWait();
  // modifier ces valeurs ligne 45 de 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(); // envoyer la position actuelle pour affichage sur le 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("Adresse du serveur WebSocket : ");
  Serial.println(Phpoc.localIP());
  Expansion.begin(460800);
  servo.attach(8);

  /** NOTE : Pour la première exécution :
   * - décommenter le dernier bloc dans xyInit()
   * - exécuter le code Arduino
   * - modifier les valeurs ligne 6, 7 de ce fichier et ligne 33 de index.php selon les valeurs de la console IDE.
   **/
  xyInit();
  lastUpdateMillis = millis();
}

void loop() {
  // attendre un nouveau 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();
}

Interface Utilisateur Web (PHP)

<!DOCTYPE html>
<html>
<head>
<title>Arduino - PHPoC Shield - Traceur 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; // en millisecondes
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; /* unité : pas */
var touchPixelX = 0, touchPixelY = 0; /* unité : 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"); /* pour dessiner la position actuelle, reçue du traceur XY */
  layer2 = document.getElementById("layer2"); /* pour dessiner la trajectoire du traceur XY, reçue du traceur XY */
  layer3 = document.getElementById("layer3"); /* pour dessiner la position de toucher actuelle, entrée par l'utilisateur sur l'écran */
  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];
  /* convertir l'unité pas en unité pixel */
  plotterX = Math.round(plotterX * CANVAS_WIDTH / MAX_X);
  plotterY = -Math.round(plotterY * CANVAS_HEIGHT / MAX_Y);
  /* dessiner la position actuelle du traceur */
  ctx1.clearRect(0, 0, CANVAS_WIDTH, -CANVAS_HEIGHT);
  ctx1.beginPath();
  ctx1.arc(plotterX, plotterY, 7, 0, 2*Math.PI);
  ctx1.fill();
  /* dessiner la trajectoire du traceur seulement quand le stylo est baissé */
  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) {
  // convertir les coordonnées
  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;
  }
  /* convertir de pixel en pas */
  var newTouchStepX = Math.round(touchPixelX / CANVAS_WIDTH * MAX_X);
  var newTouchStepY = Math.round((-touchPixelY) / CANVAS_HEIGHT * MAX_Y);
  if(type == "MOVE") {
    /* vérifier la condition de mise à jour pour éviter d'envoyer trop de données au traceur */
    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); /* seulement effacer la trajectoire */
}

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>

Processus de fabrication

  1. Construire un décodeur DTMF avec un Arduino seul – Aucun CI supplémentaire nécessaire
  2. Système d'éclairage DMX piloté par web – Contrôleur maître Arduino
  3. Robot de détection humaine basé sur Arduino Uno : Intégration capteurs étape par étape
  4. Manette de jeu USB Arduino DIY – Construisez votre propre gamepad haute performance
  5. SERENA : Système d'Alarme Arduino Mega 2560 Personnalisé avec Écran Tactile TFT LCD
  6. Véhicule Télécommandé par Android avec Raspberry Pi et Shield Moteur
  7. Créez un robot suiveur de ligne autonome avec Arduino UNO
  8. Contrôlez un rover Arduino avec Firmata et une manette Xbox One
  9. Arduino UNO & Shield WiFi : Affichage température temps réel sur le Web via série