Skip to content

Ejercicio 3 * Etch a sketch

s/c edited this page Jun 4, 2023 · 5 revisions

controller using two potentiometers to control the X and Y position of a drawing tool and a button value to clear the background. Add another button or sensor to change the way the tool draws (color, form, behavior, etc…).


Etch-a-sketch

La idea para este ejercicio era hacer una visualización retro del dispositivo, con trazos muy pixelados y un look vintage. El sensor adicional no lo utilicé para crear el trazo, sino para hacer que desaparezca. Mi idea es que si alguien sopla, los pixeles en la pantalla comiencen a desplazarse hasta que eventualmente el usuario los saque de los límites del sketch.

Etch-A-Sketch_Animator

Setup / diagrama del sistema

  • Arduino UNO
  • Dos potenciómetros
  • Módulo micrófono electret

IMG_6551


**Concepto:**

IMG_6552

Lectura con serial - conexión con Processing

Dado que en este caso el flujo de información entre Arduino y Processing es unidireccional, no debo preocuparme por leer datos desde processing.

Ejemplo básico mandar datos a processing:

int switchpin = 4;    // switch connected to pin 4

void setup()
{
  pinMode(switchpin, INPUT); // pin 0 as INPUT
  Serial.begin(9600); // start serial communication at 9600bps
}

void loop()
{
  if (digitalRead(switchpin) == HIGH) // if switch is ON
  {
    Serial.print('1'); // send 1 to Processing
  }
  else
  {
    Serial.print('0'); // otherwise send 0 to Processing
  }
  delay(100); // wait 100ms for next print
}

Ya que el setup tiene dos potenciómetros, debo manejar múltiples mensajes por el serial. Para ello usé de base estos códigos:

/**
 * Data from multiple sensors / Processing
 * by BARRAGAN http://barraganstudio.com
 * 
 * Reads values from four photoresistors connected to the
 * analog input pins 0-3. The values read from the sensors are proportional
 * to the amount of light that hits their surface.
 * The values read are printed comma separated through the serial to use them in
 * Processing
 */

//int sensorValue1, sensorValue2, sensorValue3, sensorValue4;
int sensorValue1, sensorValue2, sensorValue3, sensorValue4;


void setup()
{
  Serial.begin(9600);
}

void loop()
{
  sensorValue1 = analogRead(0);  // read sensor in analog input 0
  sensorValue2 = analogRead(1);  // read sensor in analog input 1
  //sensorValue3 = analogRead(2);  // read sensor in analog input 2
  //sensorValue4 = analogRead(3);  // read sensor in analog input 3
  Serial.print(sensorValue1, DEC);  // print sensor 1
  Serial.print(",");                // print ','
  Serial.println(sensorValue2, DEC);  // print sensor 2
  Serial.print(",");                // print ','
  //Serial.print(sensorValue3, DEC);  // print sensor 3
  //Serial.print(",");                // print ','
  //Serial.println(sensorValue4, DEC);  // print sensor 4 and newline (println)
  delay(200);   // wait 200ms for next reading
}

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1);  // delay in between reads for stability
}

Sensores

Es la primera vez que trabajo con el módulo de micrófono. Calibrarlo fue todo un reto porque las lecturas solían estar por todas partes. Este fue el código que mejor funcionó para modular las lecturas.
** Testeo del micrófono:**

const int microphonePin = A4;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int mn = 1024;
  int mx = 0;

  for (int i = 0; i < 10000; ++i) {

    int val = analogRead(microphonePin);
    
    mn = min(mn, val);
    mx = max(mx, val);
  }

  int delta = mx - mn;

  Serial.print("Min=");
  Serial.print(mn);
  Serial.print(" Max=");
  Serial.print(mx);
  Serial.print(" Delta=");
  Serial.println(delta);
}

Visualización del trazo

Para comenzar con el look clásico partí de crear un grid básico:
Captura de pantalla 2023-06-04 a la(s) 5 51 22 p m

// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com

// Example 16-6: Drawing a grid of squares

// Size of each cell in the grid, ratio of window size to video size
// 80 * 8 = 640
// 60 * 8 = 480
int videoScale = 10;

// Number of columns and rows in our system
int cols, rows;

void setup() {
  size(640, 480);

  // Initialize columns and rows
  cols = width/videoScale;
  rows = height/videoScale;
}

void draw() {

  // Begin loop for columns
  for (int i = 0; i < cols; i++) {
    // Begin loop for rows
    for (int j = 0; j < rows; j++) {

      // Scaling up to draw a rectangle at (x,y)
      int x = i*videoScale;
      int y = j*videoScale;
      fill(255);
      stroke(0);
      // For every column and row, a rectangle is drawn at an (x,y) location scaled and sized by videoScale.
      rect(x, y, videoScale, videoScale);
    }
  }
}

Las siguientes iteraciones me llevaron a esta estética:
Captura de pantalla 2023-03-08 a la(s) 5 50 23 p m | Captura de pantalla 2023-03-06 a la(s) 12 44 17 p m | Captura de pantalla 2023-03-06 a la(s) 12 43 59 p m | Captura de pantalla 2023-03-06 a la(s) 12 25 00 p m |

Y posteriormente comencé a iterar con el tamaño del grid y la espaciación entre pixeles para asegurarme que processing renderizara los pixeles como quería:

Captura de pantalla 2023-03-06 a la(s) 11 33 21 a m | Captura de pantalla 2023-03-06 a la(s) 12 19 54 p m | Captura de pantalla 2023-03-06 a la(s) 12 41 45 p m |

Código final

Arduino:

/**
 * Data from multiple sensors / Processing
 * by BARRAGAN http://barraganstudio.com
 * 
 * Reads values from four photoresistors connected to the
 * analog input pins 0-3. The values read from the sensors are proportional
 * to the amount of light that hits their surface.
 * The values read are printed comma separated through the serial to use them in
 * Processing
 */

//int sensorValue1, sensorValue2, sensorValue3, sensorValue4;
int sensorValue1, sensorValue2, sensorValue3;


void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int mn = 1024;
  int mx = 0;

  for (int i = 0; i < 10000; ++i) {

    int sensorValue3 = analogRead(2);
    
    mn = min(mn, sensorValue3);
    mx = max(mx, sensorValue3);
    }
  
  sensorValue1 = analogRead(0);  // read sensor in analog input 0
  sensorValue2 = analogRead(1);  // read sensor in analog input 1
  //sensorValue3 = analogRead(2);  // read sensor in analog input 2

  int delta = mx - mn;
  
  Serial.print(sensorValue1, DEC);  // print sensor 1
  Serial.print(",");                // print ','
  Serial.print(sensorValue2, DEC);  // print sensor 2
  Serial.print(",");                // print ','
  Serial.println(delta, DEC);  // print sensor 3
  //Serial.print(",");                // print ','
  //Serial.println(sensorValue4, DEC);  // print sensor 4 and newline (println)
  delay(200);   // wait 200ms for next reading
}

Processing

Para probar cómo se ven y ajustan los pixeles usando sólo el mouse:

int w = 10; // rectangle width
int h = 10; // rectangle height

int escalaGrid = 10;
int cols, rows;

int snapX = round(mouseX / w) * w + w/2; // '+ w/2' is offset to center of cell
int snapY = round(mouseY / h) * h + h/2;

int [ ][ ] listica = new int[6400][2];
int contador = 0;

void setup() {
  size(800, 800);
  background(255, 253, 212);
  
  cols = width/escalaGrid;
  rows = height/escalaGrid;
  
}

void draw() {
  
      // Begin loop for columns
  for (int i = 0; i < cols; i++) {
    // Begin loop for rows
    for (int j = 0; j < rows; j++) {

      // Scaling up to draw a rectangle at (x,y)
      int x = i*escalaGrid;
      int y = j*escalaGrid;
      noFill();
      stroke(0);
      // For every column and row, a rectangle is drawn at an (x,y) location scaled and sized by videoScale.
      rect(x, y, escalaGrid, escalaGrid);
    }
  }
  
  if (mousePressed) {
    fill(255);
  } else {
    fill(0);
  }
  //square(mouseX, mouseY, 3);
  square(round(mouseX / w) * w, round(mouseY / h) * h , 10);
  contador += 1;
  listica[contador][0]= mouseX;
  listica[contador][1]= mouseY;
  
}

Código integrado:

/**
 * Data from multiple sensors / Processing
 * by BARRAGAN <http://barraganstudio.com>
 * based on Tom Igoe's example from Making Things Talk book
 *
 * read serial data until a linefeed character is found
 * data are values comma separated. Split the data and convert it
 * into numbers in an array for firther use.
*/


import processing.serial.*;

Serial myPort;
int linefeed = 10;   // Linefeed in ASCII
int numSensors = 2;  // we will be expecting for reading data from four sensors
int sensors[];       // array to read the 4 values
int pSensors[];      // array to store the previuos reading, usefur for comparing
// actual reading with the last one
float posX;   //marcar pos en X del cuadrado
float posY;   //marcar pos en Y del cuadrado
float valpot1; //lectura del potenciometro 1
float valpot2; //lectura del potenciometro 2
float difpot1; //lectura del potenciometro 1
float difpot2; //lectura del potenciometro 2
float difX;   //marcar pos en X del cuadrado
float difY;   //marcar pos en Y del cuadrado

int escalaGrid = 10;
int cols, rows;

int w = 10; // rectangle width
int h = 10; // rectangle height


void setup() {
  size(800, 800);
  background(255, 253, 212);
  
  cols = width/escalaGrid;
  rows = height/escalaGrid;
  
  // List all the available serial ports in the output pane.
  // You will need to choose the port that the Wiring board is
  // connected to from this list. The first port in the list is
  // port #0 and the third port in the list is port #2.
  println(Serial.list());

  myPort = new Serial(this, Serial.list()[1], 9600);
  // read bytes into a buffer until you get a linefeed (ASCII 10):
  myPort.bufferUntil(linefeed);
}

void draw() {
  
    // Begin loop for columns
  for (int i = 0; i < cols; i++) {
    // Begin loop for rows
    for (int j = 0; j < rows; j++) {

      // Scaling up to draw a rectangle at (x,y)
      int x = i*escalaGrid;
      int y = j*escalaGrid;
      noFill();
      stroke(0);
      // For every column and row, a rectangle is drawn at an (x,y) location scaled and sized by videoScale.
      rect(x, y, escalaGrid, escalaGrid);
    }
  }
  

  if((pSensors != null)&&(sensors != null)) {

    // if valid data arrays are not null
    // compare each sensor value with the previuos reading
    // to establish change

    for(int i=0; i < numSensors; i++) {
      float f = sensors[i] - pSensors[i];  // actual - previous value
      if(f > 0) {
        println("sensor "+i+" increased by "+f);  // value increased
      }
      if(f < 0) {
        println("sensor "+i+" decreased by "+f);  // value decreased
      }
      
     //sensors[i].value = mysensors[i]; 
     valpot1 = sensors[2];
     valpot2 = sensors[1];
     difpot1 = pSensors[2];
     difpot2 = pSensors[1];

    }

    // now do something with the values read sensors[0] .. sensors[3]

    posX = map(valpot1,0,1023,0,800);
    posY = map(valpot2,0,1023,0,800);
    
    difX = map(difpot1,0,1023,0,800);
    difY = map(difpot2,0,1023,0,800);
    

      if (posX < 0) { 
      posX = width; 
      }
      if (posX > 800) { 
      posX = 0; 
      }
 
      if (posY < 0) { 
      posY = height; 
      }
      if (posY > 800) { 
      posY = 0; 
      }
    
    fill(0);
    //square(round(mouseX / w) * w, round(mouseY / h) * h , 10);
    //square(posX, posY, 2);
    // ESTA SI square(round(posX / w) * w, round(posY / h) * h , 10);
    strokeWeight(8);
    strokeCap(SQUARE);
    strokeJoin(BEVEL);
    stroke(255);
    line(posX, difY , difX, posY);

    }

  }

//}

void serialEvent(Serial myPort) {

  // read the serial buffer:
  String myString = myPort.readStringUntil(linefeed);

  // if you got any bytes other than the linefeed:
  if (myString != null) {

    myString = trim(myString);

    // split the string at the commas
    // and convert the sections into integers:

    pSensors = sensors;
    sensors = int(split(myString, ','));

    // print out the values you got:

    for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
      print("Sensor " + sensorNum + ": " + sensors[sensorNum] + "\t");
    }

    // add a linefeed after all the sensor values are printed:
    println();

  }
}

**Código que funcionó mejor con potenciómetros pero que no tiene estética vintage:

/**
 * Data from multiple sensors / Processing
 * by BARRAGAN <http://barraganstudio.com>
 * based on Tom Igoe's example from Making Things Talk book
 *
 * read serial data until a linefeed character is found
 * data are values comma separated. Split the data and convert it
 * into numbers in an array for firther use.
*/


import processing.serial.*;

Serial myPort;
int linefeed = 10;   // Linefeed in ASCII
int numSensors = 2;  // we will be expecting for reading data from four sensors
int sensors[];       // array to read the 4 values
int pSensors[];      // array to store the previuos reading, usefur for comparing
// actual reading with the last one
float posX;   //marcar pos en X del cuadrado
float posY;   //marcar pos en Y del cuadrado
float valpot1; //lectura del potenciometro 1
float valpot2; //lectura del potenciometro 2
float difpot1; //lectura del potenciometro 1
float difpot2; //lectura del potenciometro 2
float difX;   //marcar pos en X del cuadrado
float difY;   //marcar pos en Y del cuadrado

int escalaGrid = 10;
int cols, rows;

int w = 10; // rectangle width
int h = 10; // rectangle height


void setup() {
  size(800, 800);
  background(255, 253, 212);
  
  cols = width/escalaGrid;
  rows = height/escalaGrid;
  
  // List all the available serial ports in the output pane.
  // You will need to choose the port that the Wiring board is
  // connected to from this list. The first port in the list is
  // port #0 and the third port in the list is port #2.
  println(Serial.list());

  myPort = new Serial(this, Serial.list()[1], 9600);
  // read bytes into a buffer until you get a linefeed (ASCII 10):
  myPort.bufferUntil(linefeed);
}

void draw() {
  
    // Begin loop for columns
  for (int i = 0; i < cols; i++) {
    // Begin loop for rows
    for (int j = 0; j < rows; j++) {

      // Scaling up to draw a rectangle at (x,y)
      int x = i*escalaGrid;
      int y = j*escalaGrid;
      noFill();
      stroke(0);
      // For every column and row, a rectangle is drawn at an (x,y) location scaled and sized by videoScale.
      rect(x, y, escalaGrid, escalaGrid);
    }
  }
  

  if((pSensors != null)&&(sensors != null)) {

    // if valid data arrays are not null
    // compare each sensor value with the previuos reading
    // to establish change

    for(int i=0; i < numSensors; i++) {
      float f = sensors[i] - pSensors[i];  // actual - previous value
      if(f > 0) {
        println("sensor "+i+" increased by "+f);  // value increased
      }
      if(f < 0) {
        println("sensor "+i+" decreased by "+f);  // value decreased
      }
      
     //sensors[i].value = mysensors[i]; 
     valpot1 = sensors[0];
     valpot2 = sensors[1];
     difpot1 = pSensors[0];
     difpot2 = pSensors[1];

    }

    // now do something with the values read sensors[0] .. sensors[3]

    posX = map(valpot1,0,1023,0,800);
    posY = map(valpot2,0,1023,0,800);
    
    difX = map(difpot1,0,1023,0,800);
    difY = map(difpot2,0,1023,0,800);
    

      if (posX < 0) { 
      posX = width; 
      }
      if (posX > 800) { 
      posX = 0; 
      }
 
    
    fill(0);
    //square(round(mouseX / w) * w, round(mouseY / h) * h , 10);
    //square(posX, posY, 2);
    // ESTA SI square(round(posX / w) * w, round(posY / h) * h , 10);
    line(posX, posY, posX-difX, posY - difY);

    }

  }

//}

void serialEvent(Serial myPort) {

  // read the serial buffer:
  String myString = myPort.readStringUntil(linefeed);

  // if you got any bytes other than the linefeed:
  if (myString != null) {

    myString = trim(myString);

    // split the string at the commas
    // and convert the sections into integers:

    pSensors = sensors;
    sensors = int(split(myString, ','));

    // print out the values you got:

    for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
      print("Sensor " + sensorNum + ": " + sensors[sensorNum] + "\t");
    }

    // add a linefeed after all the sensor values are printed:
    println();

  }
}

Lo que no pude resolver

Aunque logré visualizar los pixeles y hacer el setup en el serial y demás, la integración con el micrófono resultó siendo mucho más complicada. Para lograr el desplazamiento tuve que haber modificado la generación de cada cuadro, almacenándolos en un array que me permitiera modificar su posición con el micrófono.

Random

Me parece cool el mecanismo: Etchasketch