Week 10
The task for week 10 was to establish communication between a computer and an Arduino Nano through a serial port and use the data from the serial input for some application.
I decided to program a game called Flappy Birds and control it with an ultrasonic sensor. To add something extra, I also used an encoder to control the game speed.
Circuit
First, I assembled my circuit on a breadboard. I used only three components: an HC-SR04 ultrasonic sensor, a rotary encoder, an Arduino Nano V3.0 ATmega328, and wires. You can see the schematic and the circuit in the photos below.
Code and Communication
First,
I had to write the code for the Arduino Nano. I read and processed the data from the ultrasonic
sensor and rotary encoder. Another necessary step was to set the correct baud rate for communication.
If you are using Platform.io like me, you need to open the platform.ini file and add the line "monitor_speed = 9600".
Then you have to send your data to the serial port. I needed to send two pieces of information,
so I sent the data in the following format: "Serial.println(String(distance) + "," + String(counter))".
Finally, I uploaded the code to the Arduino Nano.
#include <Arduino.h>
#include <Wire.h>
int angle = 0;
const int trigPin = 9;
const int echoPin = 10;
float duration, distance;
#define outputA 6
#define outputB 7
int counter = 0;
int aState;
int aLastState;
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode (outputA,INPUT);
pinMode (outputB,INPUT);
Serial.begin(9600);
// Reads the initial state of the outputA
aLastState = digitalRead(outputA);
}
void loop() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;
delay(50);
//encoder
aState = digitalRead(outputA); // Reads the "current" state of the outputA
// If the previous and the current state of the outputA are different, that means a Pulse has occured
if (aState != aLastState){
// If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
if (digitalRead(outputB) != aState) {
counter ++;
} else {
counter --;
}
}
aLastState = aState;
Serial.println(String(distance) + "," + String(counter));
}
Next,
I wrote the code for Processing 4.3. The programming language of Processing isn't C++ like
for Arduino, but Java. It is important to know the serial COM port you are using and the communication
baud rate,
because you need them for proper communication. You set them in the line of code "myPort = new Serial(this, "COM4", 9600)".
You may also encounter an error message from Processing: "Error opening serial port COM4: Port busy".
The problem
is that you are already reading data from the serial port with PlatformIO or Arduino IDE. So, you have
to end the communication there. If it still doesn't help, then disconnect and reconnect the Arduino from
your computer, and it should work. After that,
you just have to process the data from the serial port and then program the game logic using the input data.
import processing.serial.*;
int DistanceUltra;
int IncomingDistance;
Serial myPort;
String rawData;
Pipe p1 = new Pipe();
Pipe p2 = new Pipe();
Pipe p3 = new Pipe();
//bird height and width location
float birdy = 46;
float birdx = 56;
float gravity = 3;
//the speed of the pipes
float speed = 2;
//score and game state
boolean gameOver = false;
int score = 0;
int highscore = 0;
int encoder_new_state = 0;
int encoder_last_state = 0;
int point = 1;
PImage flappy;
void setup(){
size(400,600);
p1.x = width + 50;
p2.x = width + 220;
p3.x = width + 370;
myPort = new Serial(this, "COM4", 9600);
myPort.bufferUntil(10);
flappy = loadImage("flappy.png");
}
void serialEvent (Serial myPort){
rawData = myPort.readStringUntil('\n');
println(rawData);
rawData = rawData.trim(); // Remove any whitespace
String[] data = rawData.split(",");
IncomingDistance = int(data[0]);
encoder_new_state = int(data[1]);
println("incoming distance="+IncomingDistance);
if (IncomingDistance>1 && IncomingDistance<100 ) { DistanceUltra = IncomingDistance; //save the value only if its in the range 1 to 100
}
if (encoder_new_state != 0){
if (encoder_new_state > encoder_last_state){
speed += 0.1;
}
if(encoder_new_state < encoder_last_state && speed>0 ){
speed -= 0.1;
}
encoder_last_state = encoder_new_state;
}
}
void draw(){
background(0);
p1.pipe();
p2.pipe();
p3.pipe();
image(flappy, birdx, birdy, 55, 55);
// birdy += gravity;
play();
success(p1);
success(p2);
success(p3);
if (IncomingDistance>10)
{
//birdy -= jumpForce;
if (birdy > -20 && birdy <-15){
birdy -= 0.5*gravity;
}
if (birdy > -15){
birdy -= gravity;
}
}
else
{
if (birdy < height-15 && birdy < height-10){
birdy += 0.5*gravity;
}
if (birdy <height -10){
birdy += gravity;}
}
}
void play(){
if(gameOver == false)
{
p1.x -= speed;
p2.x -= speed;
p3.x -= speed;
textSize(24);
fill(255,255,255);
text("score:" + score, width/2-90, 30);
textSize(24);
fill(255,255,255);
text("speed:" + speed, width/2+90, 30);
}
if(gameOver == true)
{
speed = 0;
p1.x -= speed;
p2.x -= speed;
p3.x -= speed;
if( highscore < score)
{
highscore = score;
}
textSize(16);
fill(0, 102, 153);
textAlign(CENTER);
text("Click : Play Again", width/2, height/2);
text("Score: " + score, width/2, height/2 - 20);
text("High-Score: " + highscore, width/2, height/2 - 40);
if (mousePressed)
{
delay(900);
score = 0;
gameOver = false;
birdy = 100;
birdx = 56;
p1.x = width + 50;
p2.x = width + 220;
p3.x = width + 370;
p1.top = random(height/2-200);
p1.bottom = random(height/2+250);
p2.top = random(height/2-200);
p2.bottom = random(height/2+250);
p3.top = random(height/2-200);
p3.bottom = random(height/2+250);
speed = 2;
}
}
}
void success(Pipe test){
if(birdy < test.top || birdy > height - test.bottom)
{
if(birdx > test.x && birdx < test.x + test.w)
{
gameOver = true;
}
}
}
class Pipe
{
float top = random(height/2 - 200);
float bottom = random(height/2 +200);
float x = width + 150;
float w = 70;
float R = random(255);
float G = random(200);
float B = random(255);
color pipeColor = color(R, G, B);
void pipe()
{
fill(pipeColor);
rect(x, 0, w, top);
rect(x, height-bottom, w, bottom);
if(x < -100)
{
score += point;
x = width;
top = random(height/2-250);
bottom = random(height/2+250);
}
}
}
And finally i enjoyed the game.