Etch-A-Sketch

For this assignment, we were asked to establish a serial communication between Arduino and Processing either by making a physical controller or a physical output, all based on one of the projects we had previously done in class. Initially, I really wanted to do a physical controller, since it would be a mini simulation of an actual game controller in real life, and what better thing to brag about than this? đŸ˜€

While brainstorming for this assignment and looking through my previous projects in Processing, I noticed how, since most of them were artworks, the only project that actually included some sort of interactivity was my self-portrait. As a recap, I used mouseX and mouseY functions to enable users to draw using their mouse on screen, and then visualising their artworks through different layers. When thinking about how to incorporate this with a controller, the Etch-A-Sketch came to mind. This was an incredibly nostalgic toy most of my classmates and I would play with during recess. Not only was this a perfect way of adapting my previous project, but it was also a great opportunity of enhancing this toy and enabling it to actually save one’s masterpieces.

The famous Etch-A-Sketch

Initially, I thought this idea was perfect since it was appropriate enough for the elaboration of this task yet (supposedly) simple enough to ensure that I would not spend whole days working on this (oh how naĂ¯ve that I was).

Initially, these were the functionalities that the physical controller would have:

  1. 2 knobs: one for controlling the movement along the X axis, the other for controlling movement along the Y axis.
  2. 1 “Save” button: which would serve as a means for the user to save their drawing and continue with their second layer.
  3. 3 “visualize/layer” buttons: after the drawing functionalities were done, the user would be able to see their different drawings by turning on or off these three layer buttons.
  4. Tilt function: I was unfortunately not able to carry out this function: similar to the classic Etch-A-Sketch, I was planning on implementing a tilt sensor on the controller, so every time a person shook the controller, the drawing would be cleared.
  5. Lights: (also not implemented) depending on the mode you were on (drawing vs display) 2 LEDs would light up based on this.

The Struggles

Surprisingly enough, the struggles to carry out this project were much greater than I envisioned. What I initially thought as a simple extension of one of my previous projects ended up being another hassle altogether. Perhaps the biggest struggle was to successfully connect the Arduino and Processing and making sure that the correct data was being sent and processed between both programs.

The other problem I encountered was regarding the logic of the code. When changing between the display and draw functions, I used the “save” pushbutton. However, I had to implement a statemachine in Arduino that would ensure that it would only use the state of the button once it current state was different from its previous state.

Possible Improvements

Some possible improvements that could be implemented into this project could be adding the possibility of saving each drawing to a .csv file that would update every time the project was run. This could be further enhanced by enabling users to add more than two layers of drawing through the “save” button. For instance, once the user wanted to add a layer, they could press this button, which would add another array that would save the coordinates of the next drawing.

Here is the code I used for the Arduino:

const int ledPin = 3;
const int buttonModePin = 8;
const int button1Pin = 13;
const int button2Pin = 12;
const int button3Pin = 11;
const int restartPin = 7;

bool isAvailable = true;

int counter = 0;

void setup() {
 // put your setup code here, to run once:
 Serial.begin(9600);
 Serial.println("0,0"); 
 pinMode(buttonModePin, INPUT);
 pinMode(button1Pin, INPUT);
 pinMode(button2Pin, INPUT);
 pinMode(button3Pin, INPUT);
 pinMode(restartPin, INPUT);
 pinMode(ledPin,OUTPUT);
}

void loop() {
 int buttonModeState;
 int button1State;
 int button2State;
 int button3State;
 int restartState; 

 bool sendPressed = false;
 
 buttonModeState = digitalRead(buttonModePin);
 button1State = digitalRead(button1Pin);
 button2State = digitalRead(button2Pin);
 button3State = digitalRead(button3Pin);
 restartState = digitalRead(restartPin);

 if (restartState == HIGH){
 if(isAvailable == true){
 isAvailable = false;
 sendPressed = true;
 }
 }
 else{
 isAvailable = true;
 }
 
 
// while (Serial.available()){ //only sending a byte when we need a byte 
 int input = Serial.read(); //using the buffer so it doesn't fill up
 int xPos = analogRead(A0);
// xPos = xPos; //to assure we are in the right range (mapping)
 int yPos = analogRead(A1);
 Serial.print(xPos); //WITHOUT LN
 Serial.print(',');
 Serial.print(yPos);
 Serial.print(',');
 Serial.print(buttonModeState);
 Serial.print(',');
 Serial.print(button1State);
 Serial.print(',');
 Serial.print(button2State);
 Serial.print(',');
 Serial.print(button3State);
 Serial.print(',');
 Serial.println(sendPressed);
 
// }

 }

Here is the code that I used for Processing:

import processing.serial.*;
Serial myPort;
PImage img;

//creating layers + arrays
//drawing variables
boolean layer1 = true;
boolean layer2 = false;
boolean layer3 = false;

//mode variables
boolean restartDrawing = false;
boolean drawing = true;
boolean currentData = false;
boolean saveButton = false;
boolean previousData = false;
boolean clearLayer2 = false;

//display variables 
boolean display1 = false;
boolean display2 = false;
boolean display3 = false;

int[] xlayer1 = {};
int[] ylayer1 = {};

int[] xlayer2 = {};
int[] ylayer2 = {};

int[] xlayer3 = {};
int[] ylayer3 = {};

int led;
int led2; 

int xPos = 0;
int yPos = 0;

void setup(){
 printArray(Serial.list()); //choosing serial port from list
 String portname = Serial.list()[2];
 println(portname);
 myPort = new Serial(this,portname,9600); //inputing info
 myPort.clear(); //clears out the buffer just in case
 myPort.bufferUntil('\n'); //not gonna fire serial event until they get the new line
 size(700,573);
 background(255,255,255);
 img = loadImage("blanksketch2.png");
 
}

void draw(){
 //background(255);
 //CHECKS FOR RESTARTING OF SCREEN
 image(img, 0, 0);
 ellipse(xPos,yPos,1,1);
 
 if (restartDrawing == true){
 background(255,255,255);
 image(img, 0, 0);
 println("RESTARTED DRAWING");
 restartDrawing = false;
 }
 
 //CHECKS IF PERSON IS® IN DRAWING MODE (ON OR OFF), STARTS DRAWING
 if (drawing == true){
 println("ENTERING DRAWING LOOP");
 if (layer1 == true){
 println("DRAWING LAYER 1");
 int Xcoordinate = xPos;
 int Ycoordinate = yPos;
 xlayer1 = append(xlayer1, Xcoordinate);
 ylayer1 = append(ylayer1, Ycoordinate);
 //DRAWS THE ELLIPSE
 if ( (100 < xPos && xPos < 590) && (100 < yPos && yPos < 450)){ 
 ellipse(xPos,yPos,1,1);
 }
 }
 
 if (layer2 == true){
 if (clearLayer2 == true){
 background(255);
 image(img, 0, 0);
 clearLayer2 = false;
 }
 println("DRAWING LAYER 2");
 int Xcoordinate = xPos;
 int Ycoordinate = yPos;
 xlayer2 = append(xlayer2, Xcoordinate);
 ylayer2 = append(ylayer2, Ycoordinate);
 //DRAWS THE ELLIPSE
 if ( (100 < xPos && xPos < 590) && (100 < yPos && yPos < 450)){ 
 ellipse(xPos,yPos,1,1);
 }
 }
 
 }
 
 //DISPLAYS DRAWING DEPENDING ON BUTTONS THAT ARE ON
 if (drawing == false){
 background(255);
 
 if (display1 == true){
 println("first layer visible");
 for (int i = 0; i < (xlayer1.length - 1); i++){
 line(xlayer1[i],ylayer1[i],xlayer1[i+1],ylayer1[i+1]); 
 }
 //restartDrawing = false;
 }
 
 else if (display2 == true){
 //background(255);
 println("second layer visible");
 for (int i = 0; i < (xlayer2.length - 1); i++){
 line(xlayer2[i],ylayer2[i],xlayer2[i+1],ylayer2[i+1]); 
 }
 
 //restartDrawing = false;
 }
 println("showing image in display");
 image(img, 0, 0);
 }

 //CHECKING FOR SAVE/button press 
 if (saveButton == true){
 if (layer1 == true){
 drawing = true;
 layer1 = false;
 layer2 = true;
 clearLayer2 = true;
 println("CHANGE TO LAYER 2");
 }
 else if (layer2 == true){
 layer1 = false;
 layer2 = false;
 drawing = false;
 println("CHANGE TO DRAWING PHASE");
 }
 }
}

//no need to call the function into the draw
void serialEvent(Serial myPort){
 String s = myPort.readStringUntil('\n');
 s = trim(s); //will take out extra space in the string to avoid errors
 println(s);
 
 if (s!=null){
 //if there's nothing present in this place we're looking for
 //making sure there's something in S
 int value[] = int(split(s,',')); //taking string and splitting it at ','
 println("s!null");
 if (value.length == 7){
 xPos = (int)map(value[0],0,1023,0,width);
 yPos = (int)map(value[1],0,1023,0,height);
 
 //checking drawing mode
 //if (value[2] == 1){
 // drawing = true; 
 //}
 //else if (value[2] == 0){
 // drawing = false; 
 // //display1 = true;
 //}
 
 //checking display first drawing
 if (value[3] == 1 && value[4] == 0){
 println("VALUE 1 TRUE, GOING TO DISPLAY 1");
 //drawing = false; //esto se pone al final despues
 display1 = true;
 //drawing = false; //CAMBIAR ESTO DESPUES DE QUE SIRVA EL BOTON
 }
 else {
 println("VALUE 1 FALSE");
 display1 = false;
 //drawing = true;
 }
 
 //checking display second drawing
 if (value[4] == 1 && value[3]==0){
 println("VALUE 2 TRUE, GOING TO DISPLAY 2");
 //layer2 = true;
 display2 = true;
 //drawing = false; //CAMBIAR ESTO DESPUES QUE SIRVA EL BOTON
 }
 else{
 println("VALUE 2 FALSE");
 //layer2 = false;
 display2 = false;
 //drawing = true;
 }
 
 //checking for saved state
 if (value[6] == 1){
 saveButton = true;
 println("SAVE DATA TRUE");
 }
 else if (value[6] == 0){
 saveButton = false;
 println("SAVE DATA FALSE");
 }
 
 }
 myPort.write(led+","+led2+"\n"); //will send a byte once it receives one
 }
}