Task #1: Make a Processing Etch-A-Sketch
In task 1, I built a circuit with two potentiometers. Then, I opened an Arduino example “SendMultipleValues” and replaced the line using millis()
into another analogRead()
so that Arduino can use analogRead()
to read the values of the two potentiometers and then send them serially. Below is the code:
/*
SendMultipleValues
Reads an analog input on pin 0, and sends a record that contains two values:
1. The analog value that is read from pin A0.
2. The analog value that is read from pin A1.
If you attach a potentiometer to pin 0, you can control this value by moving
the pot.
This sketch pairs well with the RecieveMultipleValues example from the
Processing SerialRecord library
<https://osteele.github.io/Processing_SerialRecord/>.
by Oliver Steele, 2020-2022
This example code is in the public domain.
*/
#include "SerialRecord.h"
// Change this number to send a different number of values
SerialRecord writer(2);
void setup() {
Serial.begin(9600);
}
void loop() {
int sensorValue1 = analogRead(A0);
int sensorValue2 = analogRead(A1);
writer[0] = sensorValue1;
writer[1] = sensorValue2;
writer.send();
// This delay slows down the loop, so that it runs less frequently. This
// prevents it from sending data faster than a Processing sketch that runs at
// 60 frames per second will process it. It also makes it easier to debug the
// sketch, because values are received at a slower rate.
delay(20);
}
Then, I modified the Processing example “ReceiveMultipleValues” so that it draws a circle, whose x and y are read from the two analog values from Arduino. And below are the video and the code:
/**
* Example sketch for the SerialRecord library for Processing.
*
* Receives two integers from the serial port, and uses them to control the x
* and y position of a circle on the canvas.
*/
import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
void setup() {
size(500, 500);
background(0);
String serialPortName = SerialUtils.findArduinoPort();
serialPort = new Serial(this, serialPortName, 9600);
// If the Arduino sketch sends a different number of values, modify the number
// `2` on the next line to match the number of values that it sends.
serialRecord = new SerialRecord(this, serialPort, 2);
}
void draw() {
serialRecord.read();
int value1 = serialRecord.values[0];
int value2 = serialRecord.values[1];
float x = map(value1, 0, 1024, 0, width);
float y = map(value2, 0, 1024, 0, height);
fill(255);
circle(x, y, 100);
}
After that, I had to modify the code again so that it can draw continuous lines, and that was a big challenge to me. I was facing these challenges throughout the whole process:
1. How can I use Previous to make lines connect with each other?
2. Does it matter whether it is prevX=x
or x =prevX
?
3. What was wrong when the drawing was showing the first endpoint of the line was always at (0, 0) ?
4. What was wrong when the line cannot be connected together but showing a constantly moving little dot instead?
The questions all above were what I had in mind when I was asking an LA for help (I wish I would know her name 🙁 ) And by keeping those questions in mind, we tried different ways to debug the code, and the following are the answers:
1. Using prevX = x; prevY = y;
2. It doesn’t matter whether it’s prevX=x
or x =prevX
, but it’s easier to use prevX=x
as we were using float prevX =10; float prevY =10;
at the very beginning, before the setup.
3. I’m not sure if it was because of the place where we put float prevX =10; float prevY =10;
4. Because we were painting the background in the draw function so that the background is being painted again and again, covering the previous traces, so that there wasn’t a continuous line showing. However, when we put the background function inside the setup, problem solved!
After many attempts, we succeeded and my partner Nomun recorded this video of me drawing with the two potentiometers:
The code:
/**
* Example sketch for the SerialRecord library for Processing.
*
* Receives two integers from the serial port, and uses them to control the x
* and y position of a circle on the canvas.
*/
import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
float prevX =10;
float prevY =10;
void setup() {
size(500, 500);
background(0);
String serialPortName = SerialUtils.findArduinoPort();
serialPort = new Serial(this, serialPortName, 9600);
// If the Arduino sketch sends a different number of values, modify the number
// `2` on the next line to match the number of values that it sends.
serialRecord = new SerialRecord(this, serialPort, 2);
}
void draw() {
serialRecord.read();
int value1 = serialRecord.values[0];
int value2 = serialRecord.values[1];
float x = map(value1, 0, 1024, 0, width);
float y = map(value2, 0, 1024, 0, height);
stroke(255);
line(prevX, prevY, x, y);
prevX = x;
prevY = y;
}
Task #2: Bouncing Ball
Partner: Nomun
This time, we are using Processing to send signals to Arduino and it is Arduino’s turn to receive the signals: whether the bouncing ball has come to the edges. At first, I was thinking that we can write an if loop in Processing, so that when the ball comes to the edges, it will change its direction and bounce back, while it can change some signal x from 1 to 0 (signaling that the ball has come to its edges) and also send it to Arduino, so that Arduino will know that the ball is reaching the edges and it’s time for the servo to turn an angle. But then, I thought I could only use millis()
and x++
and x--
to control the back and forth of the ball, which is not as an ongoing loop, so I asked Professor Gottfried how I can realize that. And he said that we can use speed to control the direction of the ball. And we also heard that Professor Rudi was telling some other pairs that they can actually search for Bouncing Ball online so we did that too. And HERE is the code that we used as a basis.
And then when we were trying out if “speed” can work, we found that once the ball first hit the edge of the canvas, there was an error message and then the ball begins to bounce back but in an extreme slow speed. We were so confused that we asked Professor Gottfried again, and he said that we were almost there, except that the library cannot read negative values. As we want to use speed as direction, meaning we want negative to be going left, positive to be going right, we still want that to work, but there is this clever way proposed by Professor Gottfried that we can send 2+int(speed)
instead, so that it won’t be negative anymore.
Just about we thought it was finally going to work, there was this bigger challenge of how we can make the servo move accordingly. And these were the multiple problems we met and the solutions we tried with Iris:
Q1. The servo only moves when we first upload the Arduino sketch. (And another thing I noticed was that we can only upload the Arduino sketch first and then open the Processing sketch; otherwise we couldn’t even open the other one.)
A1: 1. We first changed the for loop of the servo into a direct demand because Iris told us that sometimes the for loop may negatively affect the whole process, so we did this change:
for (pos = 0; pos <= 180; pos += 1) { myservo.write(pos); delay(15); }
myservo.write(0); delay(50); myservo.write(180);
2. We found that by using int speed = reader[0]; if (speed < 1)
, the two servos will strangely move together, although Iris tried to ask them to turn different angles and they did indeed, the two of them were always working together at the same time, which is really strange, because since they can work separate tasks, that means the conditions of our if loop is correct, but they just cannot work alone. And as it was already 4p.m. and we both had classes later on, we decided to make Processing keep sending values of “x” and let Arduino tell whether the value has come to the width of the canvas.
And that was how we finally succeeded, and below are the video and the full code:
Video:
Processing: (So we were still using speed to control the direction at last but not as a signal anymore. And I also didn’t include the function fullScreen()
, because it will load even more slowly when I include that.)
/**
* Example sketch for the SerialRecord library for Processing.
*
* Maps the horizontal and vertical position of the mouse on the canvas to the
* range 0…1023, and sends them to the serial port.
*/
import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
int x = 0;
int speed = 2;
void setup() {
size(500, 500);
String serialPortName = SerialUtils.findArduinoPort();
serialPort = new Serial(this, serialPortName, 9600);
serialRecord = new SerialRecord(this, serialPort, 1);
}
void draw() {
//fullScreen();
background(0);
x = x + speed;
if ((x > width) || (x < 0)) {
speed = speed * -1;
}
circle(x, height/2, 100);
serialRecord.values[0] = x;
serialRecord.send();
}
Arduino:
/*
ReceiveSingleValue
This sketch repeatedly receives a record that contains two values. Both values
should be in the range 0…1023:
- The first value controls the builtin LED. The LED is turned on if the value
is in the upper half of the range (512…1023).
- The second value controls a buzzer attached to pin 9. If the value is 0, the
buzzer is silenced; otherwise, it plays a tone at the specified frequency.
This sketch pairs well with the SendMultipleValues example from the
Processing SerialRecord library
<https://osteele.github.io/Processing_SerialRecord/>.
by Oliver Steele, 2020-2022
This example code is in the public domain.
*/
#include <Servo.h>
#include "SerialRecord.h"
Servo myservo;
Servo myservo2;
int x;
SerialRecord reader(1);
void setup() {
Serial.begin(9600);
myservo.attach(8);
myservo2.attach(9);
}
void loop() {
reader.read();
x = reader[0];
if (x == 0) {
myservo.write(0);
delay(50);
myservo.write(180);
}else if (x == 500) {
myservo2.write(0);
delay(50);
myservo2.write(180);
}
}
Inflection:
There are so many things to be careful with but it’s also always good to try things out and debug them. And I was thinking about the weird thing that the two servos can only work at the same time. Probably it was because the reader.read()
function is in the loop and therefore constantly checking the value sent? But where else could it be? And the last time when the servos weren’t working at all, we tried to run the simplest example and then found that we were connecting the two potentiometers as the servos, so there was something wrong in the circuit. However, this time, we’ve already checked the example and we found that it was actually working fine, so there’s basically no more space for us to debug, which makes it even harder.