#define left1 32 #define left2 26 #define right2 30 #define right1 28 #define eLeft A8 #define eRight A9 //******************************************************************************************************************************************************************************************** #include <LiquidCrystal.h> int lcd_key = 0; int adc_key_in = 0; // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //******************************************************************************************************************************************************************************************** String data=""; int mark = 0; boolean Mark_Start=false; boolean valid=false; String GGAUTCtime,GGAlatitude,GGAlongitude,GPStatus,SatelliteNum,HDOPfactor,Height, PositionValid,RMCUTCtime,RMClatitude,RMClongitude,Speed,Direction,Date,Declination,Mode; //******************************************************************************************************************************************************************************************** int longitude, latitude; //******************************************************************************************************************************************************************************************** void setup() { pinMode(right1, OUTPUT); pinMode(right2, OUTPUT); pinMode(left1, OUTPUT); pinMode(left2, OUTPUT); pinMode(eLeft, OUTPUT); pinMode(eRight, OUTPUT); digitalWrite(eLeft, HIGH); digitalWrite(eRight, HIGH); //******************************************************************************************************************************************************************************************** Serial.begin(9600); Serial1.begin(9600); Serial.println(0); delay(1000); pinMode(21, OUTPUT); digitalWrite (21, HIGH); pinMode(20, OUTPUT); digitalWrite (20, LOW); lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Initializing GPS"); // print a simple message } bool clipped = 0; //******************************************************************************************************************************************************************************************** void loop() { gpsBoi(); clipped = 0; } //******************************************************************************************************************************************************************************************** void turnLeft(int speed){ analogWrite(left2, speed); digitalWrite(left1, LOW); analogWrite(right1, speed); digitalWrite(right2, LOW); } //****************************** void turnRight(int speed){ analogWrite(left1, speed); digitalWrite(left2, LOW); analogWrite(right2, speed); digitalWrite(right1, LOW); } //****************************** void moveForwards(int speed){ analogWrite(right1, speed); digitalWrite(right2, LOW); analogWrite(left1, speed); digitalWrite(left2, LOW); } //****************************** void moveBackwards(int speed){ analogWrite(right2, speed); digitalWrite(right1, LOW); analogWrite(left2, speed); digitalWrite(left1, LOW); } //****************************** void turn90right(){ turnRight(150); delay(985); } void turn90left(){ turnLeft(150); delay(985); } //****************************** void halt(){ digitalWrite(right2, LOW); digitalWrite(right1, LOW); digitalWrite(left2, LOW); digitalWrite(left1, LOW); } //****************************** void gpsBoi(){ while (Serial1.available()> 0){ if(Mark_Start){ data=reader(); Serial.println(data); if(data.equals("GPGGA")){ //Serial.println(1); GGAUTCtime=reader(); GGAlatitude=reader(); GGAlatitude+=reader(); GGAlongitude=reader(); GGAlongitude+=reader(); GPStatus=reader(); SatelliteNum=reader(); HDOPfactor=reader(); Height=reader(); Mark_Start=false; valid=true; data=""; } else if(data.equals("GPGSA")){ Serial.println(2); Mark_Start=false; data=""; } else if(data.equals("GPGSV")){ Serial.println(3); Mark_Start=false; data=""; } else if(data.equals("GPRMC")){ //Serial.println(4); RMCUTCtime=reader(); PositionValid=reader(); RMClatitude=reader(); RMClatitude+=reader(); RMClongitude=reader(); RMClongitude+=reader(); Speed=reader(); Direction=reader(); Date=reader(); Declination=reader(); Declination+=reader(); Mode=reader(); valid=true; Mark_Start=false; data=""; } else if(data.equals("GPVTG")){ Serial.println(5); Mark_Start=false; data=""; } else{ Serial.println(6); Mark_Start=false; data=""; } } if(valid){ if(PositionValid=="A"){ Serial.println("Position Valid"); lcd.clear(); } else{ Serial.println("Your position is not valid."); } //lcd.clear(); clearCoord(); Serial.print("Date:"); Serial.println(Date); Serial.print("UTCtime:"); Serial.print(RMCUTCtime); Serial.print(" "); Serial.println(GGAUTCtime); Serial.print("Latitude:"); Serial.print(RMClatitude); lcd.setCursor(0,0); lcd.print(latitude); Serial.print(" "); Serial.println(GGAlatitude); Serial.print("Longitude:"); Serial.print(RMClongitude); lcd.setCursor(0,1); lcd.print(longitude); Serial.println("LONGITUDE THE REAL ONE"); Serial.println(longitude); Serial.println("LATITUDE THE REAL ONE"); Serial.println(latitude); Serial.print(" "); Serial.println(GGAlongitude); Serial.print("GPStatus:"); Serial.println(GPStatus); Serial.print("SatelliteNum:"); Serial.println(SatelliteNum); Serial.print("HDOPfactor:"); Serial.println(HDOPfactor); Serial.print("Height:"); Serial.println(Height); Serial.print("Speed:"); Serial.println(Speed); Serial.print("Direction:"); Serial.println(Direction); Serial.print("Declination:"); Serial.println(Declination); Serial.print("Mode:"); Serial.println(Mode); valid=false; } if(Serial1.find("$")){ Serial.println("capture"); Mark_Start=true; } } } String reader(){ String value=""; int temp; startover: while (Serial1.available() > 0){ delay(2); temp=Serial1.read(); if((temp==',')||(temp=='*')){ if(value.length()){ //Serial.println("meaningful message"); return value; } else { //Serial.println("empty"); return ""; } } else if(temp=='$'){ //Serial.println("failure"); Mark_Start=false; } else{ //Serial.println("add"); value+=char(temp); } } while (!(Serial1.available()>0)){ } goto startover; } //****************************** void clearCoord(){ if(clipped == 0){ RMClatitude.remove(RMClatitude.length()-1); RMClongitude.remove(RMClongitude.length()-1); RMClatitude.remove(0,5); RMClongitude.remove(0,6); latitude = RMClatitude.toInt(); longitude = RMClongitude.toInt(); } clipped = 1; }
Category Archives: Arduino
Rocker-Bogie GPS and Motor Control Code
#define left1 32 #define left2 26 #define right2 30 #define right1 28 #define eLeft A8 #define eRight A9 //******************************************************************************************************************************************************************************************** #include <LiquidCrystal.h> int lcd_key = 0; int adc_key_in = 0; // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //******************************************************************************************************************************************************************************************** String data=""; int mark = 0; boolean Mark_Start=false; boolean valid=false; String GGAUTCtime,GGAlatitude,GGAlongitude,GPStatus,SatelliteNum,HDOPfactor,Height, PositionValid,RMCUTCtime,RMClatitude,RMClongitude,Speed,Direction,Date,Declination,Mode; //******************************************************************************************************************************************************************************************** int longitude, latitude; //******************************************************************************************************************************************************************************************** void setup() { pinMode(right1, OUTPUT); pinMode(right2, OUTPUT); pinMode(left1, OUTPUT); pinMode(left2, OUTPUT); pinMode(eLeft, OUTPUT); pinMode(eRight, OUTPUT); digitalWrite(eLeft, HIGH); digitalWrite(eRight, HIGH); //******************************************************************************************************************************************************************************************** Serial.begin(9600); Serial1.begin(9600); Serial.println(0); delay(1000); pinMode(21, OUTPUT); digitalWrite (21, HIGH); pinMode(20, OUTPUT); digitalWrite (20, LOW); lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Initializing GPS"); // print a simple message } //******************************************************************************************************************************************************************************************** void loop() { gpsGet(); } //******************************************************************************************************************************************************************************************** void turnLeft(int speed){ analogWrite(left2, speed); digitalWrite(left1, LOW); analogWrite(right1, speed); digitalWrite(right2, LOW); } //****************************** void turnRight(int speed){ analogWrite(left1, speed); digitalWrite(left2, LOW); analogWrite(right2, speed); digitalWrite(right1, LOW); } //****************************** void moveForwards(int speed){ analogWrite(right1, speed); digitalWrite(right2, LOW); analogWrite(left1, speed); digitalWrite(left2, LOW); } //****************************** void moveBackwards(int speed){ analogWrite(right2, speed); digitalWrite(right1, LOW); analogWrite(left2, speed); digitalWrite(left1, LOW); } //****************************** void turn90right(){ turnRight(150); delay(985); } void turn90left(){ turnLeft(150); delay(985); } //****************************** void haltyoudemon(){ digitalWrite(right2, LOW); digitalWrite(right1, LOW); digitalWrite(left2, LOW); digitalWrite(left1, LOW); } //****************************** void gpsGet(){ while (Serial1.available()> 0){ if(Mark_Start){ data=reader(); Serial.println(data); if(data.equals("GPGGA")){ //Serial.println(1); GGAUTCtime=reader(); GGAlatitude=reader(); GGAlatitude+=reader(); GGAlongitude=reader(); GGAlongitude+=reader(); GPStatus=reader(); SatelliteNum=reader(); HDOPfactor=reader(); Height=reader(); Mark_Start=false; valid=true; data=""; } else if(data.equals("GPGSA")){ Serial.println(2); Mark_Start=false; data=""; } else if(data.equals("GPGSV")){ Serial.println(3); Mark_Start=false; data=""; } else if(data.equals("GPRMC")){ //Serial.println(4); RMCUTCtime=reader(); PositionValid=reader(); RMClatitude=reader(); RMClatitude+=reader(); RMClongitude=reader(); RMClongitude+=reader(); Speed=reader(); Direction=reader(); Date=reader(); Declination=reader(); Declination+=reader(); Mode=reader(); valid=true; Mark_Start=false; data=""; } else if(data.equals("GPVTG")){ Serial.println(5); Mark_Start=false; data=""; } else{ Serial.println(6); Mark_Start=false; data=""; } } if(valid){ if(PositionValid=="A"){ Serial.println("Position Valid"); } else{ Serial.println("Your position is not valid."); } lcd.clear(); CoordNum(); Serial.print("Date:"); Serial.println(Date); Serial.print("UTCtime:"); Serial.print(RMCUTCtime); Serial.print(" "); Serial.println(GGAUTCtime); Serial.print("Latitude:"); Serial.print(RMClatitude); lcd.setCursor(0,0); lcd.print(RMClatitude); Serial.print(" "); Serial.println(GGAlatitude); Serial.print("Longitude:"); Serial.print(RMClongitude); lcd.setCursor(0,1); lcd.print(RMClongitude); Serial.print(" "); Serial.println(GGAlongitude); Serial.print("GPStatus:"); Serial.println(GPStatus); Serial.print("SatelliteNum:"); Serial.println(SatelliteNum); Serial.print("HDOPfactor:"); Serial.println(HDOPfactor); Serial.print("Height:"); Serial.println(Height); Serial.print("Speed:"); Serial.println(Speed); Serial.print("Direction:"); Serial.println(Direction); Serial.print("Declination:"); Serial.println(Declination); Serial.print("Mode:"); Serial.println(Mode); valid=false; } if(Serial1.find("$")){ Serial.println("capture"); Mark_Start=true; } } } String reader(){ String value=""; int temp; startover: while (Serial1.available() > 0){ delay(2); temp=Serial1.read(); if((temp==',')||(temp=='*')){ if(value.length()){ //Serial.println("meaningful message"); return value; } else { //Serial.println("empty"); return ""; } } else if(temp=='$'){ //Serial.println("failure"); Mark_Start=false; } else{ //Serial.println("add"); value+=char(temp); } } while (!(Serial1.available()>0)){ } goto startover; } //****************************** void CoordNum(){ RMClatitude.remove(RMClatitude.length()-1); RMClongitude.remove(RMClongitude.length()-1); latitude = RMClatitude.toInt(); longitude = RMClongitude.toInt(); }
L293N Arduino Motor Control Code – Non-Variable Speed
//L293D //Motor A const int motorPin1 = 9; // Pin 14 of L293 const int motorPin2 = 10; // Pin 10 of L293 //Motor B const int motorPin3 = 6; // Pin 7 of L293 const int motorPin4 = 5; // Pin 2 of L293 //This will run only one time. void setup(){ //Set pins as outputs pinMode(motorPin1, OUTPUT); pinMode(motorPin2, OUTPUT); pinMode(motorPin3, OUTPUT); pinMode(motorPin4, OUTPUT); //Motor Control - Motor A: motorPin1,motorpin2 &amp; Motor B: motorpin3,motorpin4 //This code will turn Motor A clockwise for 2 sec. analogWrite(motorPin1, 180); analogWrite(motorPin2, 0); analogWrite(motorPin3, 180); analogWrite(motorPin4, 0); delay(5000); //This code will turn Motor A counter-clockwise for 2 sec. analogWrite(motorPin1, 0); analogWrite(motorPin2, 180); analogWrite(motorPin3, 0); analogWrite(motorPin4, 180); delay(5000); //This code will turn Motor B clockwise for 2 sec. analogWrite(motorPin1, 0); analogWrite(motorPin2, 180); analogWrite(motorPin3, 180); analogWrite(motorPin4, 0); delay(1000); //This code will turn Motor B counter-clockwise for 2 sec. analogWrite(motorPin1, 180); analogWrite(motorPin2, 0); analogWrite(motorPin3, 0); analogWrite(motorPin4, 180); delay(1000); //And this code will stop motors analogWrite(motorPin1, 0); analogWrite(motorPin2, 0); analogWrite(motorPin3, 0); analogWrite(motorPin4, 0); } void loop(){ }
L298N Arduino Control Code 2 Non-Variable Speed
/* Arduino DC Motor Control - PWM | H-Bridge | L298N - Example 01 by Dejan Nedelkovski, www.HowToMechatronics.com */ #define enA 9 #define in1 6 #define in2 7 #define button 4 int rotDirection = 0; int pressed = false; void setup() { pinMode(enA, OUTPUT); pinMode(in1, OUTPUT); pinMode(in2, OUTPUT); pinMode(button, INPUT); // Set initial rotation direction digitalWrite(in1, LOW); digitalWrite(in2, HIGH); } void loop() { int potValue = analogRead(A0); // Read potentiometer value int pwmOutput = map(potValue, 0, 1023, 0 , 255); // Map the potentiometer value from 0 to 255 analogWrite(enA, pwmOutput); // Send PWM signal to L298N Enable pin // Read button - Debounce if (digitalRead(button) == true) { pressed = !pressed; } while (digitalRead(button) == true); delay(20); // If button is pressed - change rotation direction if (pressed == true &amp; rotDirection == 0) { digitalWrite(in1, HIGH); digitalWrite(in2, LOW); rotDirection = 1; delay(20); } // If button is pressed - change rotation direction if (pressed == false &amp; rotDirection == 1) { digitalWrite(in1, LOW); digitalWrite(in2, HIGH); rotDirection = 0; delay(20); } }
Arduino OSEPP LCD Display 16×2 Sample Code
//Sample using LiquidCrystal library #include <LiquidCrystal.h> // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void setup() { lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Push the buttons"); // print a simple message } void loop() { lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over lcd.print(millis()/1000); // display seconds elapsed since power-up lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { lcd.print("RIGHT "); break; } case btnLEFT: { lcd.print("LEFT "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { lcd.print("SELECT"); break; } case btnNONE: { lcd.print("NONE "); break; } } }
Arduino MEGA2560 to Drive U-BLOX NEO-6M GPS Module
1) )Objective:
Use Arduino Mega2560 to get Latitude ,longitude and altitude from U-BLOX NEO-6M GPS module
2)Parts
1 x Arduino Mega2560 board
1 x U-BLOX NEO-6M GPS module
3) About U-BLOX NEO-6M GPS module
The module uses U-BLOX NEO-6M module.The module comes with high-performance ceramic antenna, which is equival of the integrated active antenna.Module comes with EEPROM. All configuration information can be stored in the EEPROM. A variety of configurations to meet your needs.The module also comes with a rechargeable backup battery (to support warm or warm start. after the main power supply power off, back-up battery power can maintain a half hour for GPS receiver data stored).
Features
- Use U-BLOX NEO-6M modular, compact, and excellent performance.
- Comes with ceramic antenna, capability of searching star is quite good.
- You can set various parameters via the serial port, and can be stored in the EEPROM, and easy to use.
- Compatible with 3.3V / 5V level, for easy connection to a variety of microprocessor systems.
- built-in rechargeable backup battery, can retentive ephemeris data
Technical Parameters
Parameters | Value |
---|---|
Power Supply | 3V/5V |
Model | GPS-NEO-6M-001 |
Antenna | ceramic antenna |
Battery | rechargeable battery back-up |
Signal light | LED light |
Antenna size | 25*25mm |
Model size | 25.5mm*31.5mm |
Mounting Hole | 2mm |
The default baud rate | 9600 |
The default output | Compatible with NMEA0183 protocol |
Notice
- Outdoor use
- Note Lightning and waterproof
- Do not support Raspberry Pi 3 B (Because of RPi 3B’s serial port problem, it may have other solution)
4) Circuit connection and graph
mega2560 | GPS Module |
---|---|
5V | VCC |
GND | GND |
RX1(19) | TXD |
TX1 (18) | RXD |
4) Source code:
String data=""; int mark = 0; boolean Mark_Start=false; boolean valid=false; String GGAUTCtime,GGAlatitude,GGAlongitude,GPStatus,SatelliteNum,HDOPfactor,Height, PositionValid,RMCUTCtime,RMClatitude,RMClongitude,Speed,Direction,Date,Declination,Mode; void setup(){ Serial.begin(9600); Serial1.begin(9600); Serial.println(0); delay(1000); } void loop(){ while (Serial1.available()> 0){ if(Mark_Start){ data=reader(); Serial.println(data); if(data.equals("GPGGA")){ //Serial.println(1); GGAUTCtime=reader(); GGAlatitude=reader(); GGAlatitude+=reader(); GGAlongitude=reader(); GGAlongitude+=reader(); GPStatus=reader(); SatelliteNum=reader(); HDOPfactor=reader(); Height=reader(); Mark_Start=false; valid=true; data=""; } else if(data.equals("GPGSA")){ Serial.println(2); Mark_Start=false; data=""; } else if(data.equals("GPGSV")){ Serial.println(3); Mark_Start=false; data=""; } else if(data.equals("GPRMC")){ //Serial.println(4); RMCUTCtime=reader(); PositionValid=reader(); RMClatitude=reader(); RMClatitude+=reader(); RMClongitude=reader(); RMClongitude+=reader(); Speed=reader(); Direction=reader(); Date=reader(); Declination=reader(); Declination+=reader(); Mode=reader(); valid=true; Mark_Start=false; data=""; } else if(data.equals("GPVTG")){ Serial.println(5); Mark_Start=false; data=""; } else{ Serial.println(6); Mark_Start=false; data=""; } } if(valid){ if(PositionValid=="A"){ Serial.println("Position Valid"); } else{ Serial.println("Your position is not valid."); } Serial.print("Date:"); Serial.println(Date); Serial.print("UTCtime:"); Serial.print(RMCUTCtime); Serial.print(" "); Serial.println(GGAUTCtime); Serial.print("Latitude:"); Serial.print(RMClatitude); Serial.print(" "); Serial.println(GGAlatitude); Serial.print("Longitude:"); Serial.print(RMClongitude); Serial.print(" "); Serial.println(GGAlongitude); Serial.print("GPStatus:"); Serial.println(GPStatus); Serial.print("SatelliteNum:"); Serial.println(SatelliteNum); Serial.print("HDOPfactor:"); Serial.println(HDOPfactor); Serial.print("Height:"); Serial.println(Height); Serial.print("Speed:"); Serial.println(Speed); Serial.print("Direction:"); Serial.println(Direction); Serial.print("Declination:"); Serial.println(Declination); Serial.print("Mode:"); Serial.println(Mode); valid=false; } if(Serial1.find("$")){ Serial.println("capture"); Mark_Start=true; } } } String reader(){ String value=""; int temp; startover: while (Serial1.available() > 0){ delay(2); temp=Serial1.read(); if((temp==',')||(temp=='*')){ if(value.length()){ //Serial.println("meaningful message"); return value; } else { //Serial.println("empty"); return ""; } } else if(temp=='$'){ //Serial.println("failure"); Mark_Start=false; } else{ //Serial.println("add"); value+=char(temp); } } while (!(Serial1.available()>0)){ } goto startover; }
5) Result:
Bring U-BLOX NEO-6M GPS module outdoor, open serial monitor window in Arduino IDE(upper right corner)
You will see following data information:
Bogie Assembly Instructions
4x4x4 LED Cube Arduino Display Driver
Display driver code for lighting 1-64 LEDs in 4x4x4 cube.
/* 4x4x4 LED Cube Connection Setup: Columns [(x,y)-Pin] (1,1)-13 (1,2)-12 (1,3)-11 (1,4)-10 (2,1)-9 (2,2)-8 (2,3)-7 (2,4)-6 (3,1)-5 (3-2)-4 (3-3)-3 (3,4)-2 (4,1)-1 (4,2)-0 (4,3)-A5 (4,4)-A4 Layers [layer-Pin] a-A0 b-A1 c-A2 d-A3 JOYSTICK CONTROL VRX - A5 VRY - A7 JSW - D21 (VRZ SIKE 🙂 (o'v'o) */ //bool thing[64] //initializing and declaring led rows int column[16]={15,14,13,12,11,10,9,8,7,6,5,4,3,2,17,16}; //initializing and declaring led layers int layer[4]={A3,A2,A1,A0}; int time = 250; bool test[64]; int jsw = 21; int vrx = A7; int vry = A5; int returnDir(){ int x; int y; //Z MAP // 0 1 2 // 3 4 5 // 6 7 8 int z; int w; x = analogRead(vrx); //Serial.print("x value: "); //Serial.print(x); //Serial.print(" "); y = analogRead(vry); //Serial.print(" y value: "); //Serial.println(y); if(x > 682){ z = 0; } if(x > 341 && x < 683){ z = 1; } if(x < 342){ z = 2; } if(y > 682){ w = 2; } if(y > 341 && y < 683){ w = 1; } if(y < 342){ w = 0; } return 3*w+z; } void setup() { //setting rows to ouput for(int i = 0; i<16; i++) { pinMode(column[i], OUTPUT); } //setting layers to output for(int i = 0; i<4; i++) { pinMode(layer[i], OUTPUT); } //seeding random for random pattern //randomSeed(analogRead(10)); for(int i = 0; i<64; i++){ test[i] = 0; } pinMode(vrx, INPUT); pinMode(vry, INPUT); pinMode(jsw, INPUT); Serial.begin(9600); } void turnEverythingOff() { for(int i = 0; i<16; i++) { digitalWrite(column[i], 1); } for(int i = 0; i<4; i++) { digitalWrite(layer[i], 0); } } void turnEverythingOn() { for(int i = 0; i<16; i++) { digitalWrite(column[i], 0); } //turning on layers for(int i = 0; i<4; i++) { digitalWrite(layer[i], 1); } } void OneOn(int x, int y, int z) { digitalWrite(layer[z],1); for (int i=0; i<16; i++) { digitalWrite(column[i],1); } digitalWrite(-4*x-y+20, 0); } void Light(bool leds[64], int dur){ for (int j=0; j<ceil(dur/10); j++){ for (int i=0; i<64; i++){ if (leds[i]){ digitalWrite(column[i%16],0); digitalWrite(layer[int(floor(i/16))],1); } else { digitalWrite(column[i%16],1); digitalWrite(layer[int(floor(i/16))],0); } delayMicroseconds(156); } } } //xxxxxxxxxxxxxxxxxxxxFUNCTION LOOPxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx int dir; int b = 63; void loop(){ dir = returnDir(); if(b<63){ test[b+1]=0; } test[b]=1; Light(test,10); Light(test,100); if(dir == 1){ b=b-1; } }
Make Your Own Arduboy Game – Make Pong From Scratch!
Or Operator
We already talked about using the and
operator. It’s a way we connect two comparisons inside of an if
statement. To illustrate it, look at this code:
if( a == 5 and b > 20 ) {
c = 0;
}
c
will only be set to 0
if a
is equal to 5
and b
is greater than 20
.
There are some other operators that we can use instead of the and
operator. One of them is the or
operator. The or
operator can be used to check if one of a few comparisons are true. Check out this code:
if( a == 5 or b > 20 ) {
c = 20;
}
In the above example, c
will be set to 20
if either a
is equal to 5
or b
is greater than 20
.
Keep the or operator in mind. We’ll use it soon!
Modulo Operation
Do you know the answer to 12 ÷ 6? The answer is 2! That was easy, right? Okay, what about 23 ÷ 5? The answer is either 4.6 or 4 with a remainder of 3. Which is the correct answer??
If you asked the Arduboy what 23 ÷ 5 was, the answer would be 4! That seems wrong! Where did the remainder go?? We need give the Arduboy another instruction to figure out the remainder.
Remember when I said that computers have to be very precise when given instructions? When computers were young, people tried to come up with the best way to do devision. There were a lot of ideas, but the best way seemed to be introducing the modulo operator.
To get the remainer, we ask the Arduboy for the answer of 23 % 5. It will output 3! If you asked 12 % 6, you would get 0!
Why is this important? Well, we can use this to figure out more complex math, but there’s something special in game development that we can use this for.
Anytime you use two numbers with the modulo operator, the answer will be somewhere between 0 and the second number. a % b will never be a number more than b.
Keep this in mind and we’ll do something really fun with it later!
7. Adjusting The AI
Okay, so, the computer player is way too good. We’ll never be able to win. One thing we can do to stop it from being so good is to only allow it to move some of the time. When will we allow it to move its paddle? Well, for starters, we could let it move its paddle only whenever the ball is close to the right side of the screen!
Find these lines of code:
if(bally < computery) {
computery = computery - 1;
}
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
These are the lines of code that move the computer’s paddle. Let’s put an if
statement around them that checks if the ball’s horizontal position (ballx
) is close to the right-side of the screen. I put 115.
if(ballx > 115) {
if(bally < computery) {
computery = computery - 1;
}
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
}
Test it out and think about it for a second. Notice how the computer’s paddle doesn’t always move like it used it? It’s a little more precise right now, but only moves whenever the ball is close to scoring.
But, we need to add more variance… More randomness… We need to make it so that the game is different every time you play. I KNOW! Let’s let the computer paddle move when the ball is close or let the computer paddle move when the computer picks a random number.
Back in Part 5 of this series, I showed you how to pick a random number. I’m going to explain to you how that works, now.
First of all, we need to know about the rand()
function. This will give us a random number, but the number can really be anything. It can be 2,000, or 9 or -200. There’s really no control over it. HOWEVER, we can use the modulo operator that we learned about earlier!! Take a look at this code and think about what this will output!!
rand() % 20
Well, we’ll randomly pick a number, then divide it by 20, then only have the remainder left. It doesn’t matter what number is randomly picked, if we just looked at the remainder, the remainder will be some number between 0 and 20. Which number? I don’t know. Isn’t that awesome?!
That little line of code will essentially pick a random number between 0 and 20!
Let’s adjust that if
statement from above!
if(ballx > 115 or rand() % 20 == 1) {
This means that if ballx
is greater than 115, or a randomly-picked number is equal to 1, then we move the comptuer’s paddle. Basically, the computer’s paddle will randomly move 1/20th of the time.
Here’s the full code so far!
//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone
#include <Arduboy.h>
Arduboy arduboy;
//Variables declared here
int gamestate = 0;
int justpressed = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computerx = 127 - paddlewidth;
int computery = 0;
void setup() {
arduboy.begin();
//Seed the random number generator
srand(7/8);
//Set the game to 60 frames per second
arduboy.setFrameRate(60);
arduboy.clear();
}
void loop() {
//Prevent the Arduboy from running too fast
if(!arduboy.nextFrame()) {
return;
}
arduboy.clear();
//Game code here
switch( gamestate ) {
case 0:
//Title screen
arduboy.setCursor(0, 0);
arduboy.print("Title Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 1;
}
break;
case 1:
//Gameplay screen
arduboy.setCursor(0, 0);
arduboy.print("Gameplay");
//Draw the ball
arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
//Move the ball right
if(ballright == 1) {
ballx = ballx + 1;
}
//Move the ball left
if(ballright == -1) {
ballx = ballx - 1;
}
//Reflect the ball off of the left side of the screen
if(ballx == 0) {
ballright = 1;
}
//Reflect the ball off of the right side of the screen
if(ballx + ballsize == 127) {
ballright = -1;
}
//Move the ball down
if(balldown == 1) {
bally = bally + 1;
}
//Move the ball up
if(balldown == -1) {
bally = bally - 1;
}
//Reflect the ball off of the top of the screen
if(bally == 0) {
balldown = 1;
}
//Reflect the ball off of the bottom of the screen
if(bally + ballsize == 63) {
balldown = -1;
}
//Draw the player's paddle
arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
//If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
if(arduboy.pressed(UP_BUTTON) and playery > 0) {
playery = playery - 1;
}
//If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
if(arduboy.pressed(DOWN_BUTTON) and playery + paddleheight < 63) {
playery = playery + 1;
}
//Draw the computer's paddle
arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
if(ballx > 115 or rand() % 20 == 1) {
//If the ball is higher than the computer's paddle, move the computer's paddle up
if(bally < computery) {
computery = computery - 1;
}
//If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
}
//If the ball makes contact with the player's paddle, bounce it back to the right
if(ballx == playerx + paddlewidth and playery < bally + ballsize and playery + paddleheight > bally) {
ballright = 1;
}
//If the ball makes contact with the computer's paddle, bounce it back to the left
if(ballx + ballsize == computerx and computery < bally + ballsize and computery + paddleheight > bally) {
ballright = -1;
}
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 2;
}
break;
case 2:
//Win screen
arduboy.setCursor(0, 0);
arduboy.print("Win Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 3;
}
break;
case 3:
//Game over screen
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 0;
}
break;
}
//Check if the button is being held down
if(arduboy.notPressed(A_BUTTON)) {
justpressed = 0;
}
arduboy.display();
}
8. Scoring
Since we’re going to introduce scoring, we need a way for the ball to actually be scored. This means we need to remove some of our old bouncing code.
if(ballx == 0) {
ballright = 1;
}
if(ballx + ballsize == 127) {
ballright = -1;
}
Find those lines and remove them. This way, the ball will continue to move beyond the screen. In fact, we’ll check to see if the ball has moved off of the screen in order to give points!
Feel free to test the code out once you remove that code.
To keep track of the scores, let’s declare some variables at the top of the sketch.
int playerscore = 0;
int computerscore = 0;
Now, look for the code in case 1
that displayed the text, “Gameplay.” Instead of just displaying that text, let’s modify it to display the player’s and computer’s scores.
arduboy.setCursor(20, 0);
arduboy.print(playerscore);
arduboy.setCursor(101, 0);
arduboy.print(computerscore);
Next, we need a way to actually score! Since the code now allows for the ball to move off of the screen, let’s check if it’s off the screen and if it is, then give someone a point, and then move it back into the middle of the screen.
Here’s how we can check if the ball is off of the screen.
if(ballx < -10) {
}
if(ballx > 130) {
}
To change the points, and put it back into the middle of the screen, we simply change the appropriate variables…
if(ballx < -10) {
ballx = 63;
computerscore = computerscore + 1;
}
if(ballx > 130) {
ballx = 63;
playerscore = playerscore + 1;
}
I really suggest testing the game out, so far. Here’s the code that I have:
//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone
#include <Arduboy.h>
Arduboy arduboy;
//Variables declared here
int gamestate = 0;
int justpressed = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;
void setup() {
arduboy.begin();
//Seed the random number generator
srand(7/8);
//Set the game to 60 frames per second
arduboy.setFrameRate(60);
arduboy.clear();
}
void loop() {
//Prevent the Arduboy from running too fast
if(!arduboy.nextFrame()) {
return;
}
arduboy.clear();
//Game code here
switch( gamestate ) {
case 0:
//Title screen
arduboy.setCursor(0, 0);
arduboy.print("Title Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 1;
}
break;
case 1:
//Gameplay screen
//Display the player's score
arduboy.setCursor(20, 0);
arduboy.print(playerscore);
//Display the computer's score
arduboy.setCursor(101, 0);
arduboy.print(computerscore);
//Draw the ball
arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
//Move the ball right
if(ballright == 1) {
ballx = ballx + 1;
}
//Move the ball left
if(ballright == -1) {
ballx = ballx - 1;
}
//Move the ball down
if(balldown == 1) {
bally = bally + 1;
}
//Move the ball up
if(balldown == -1) {
bally = bally - 1;
}
//Reflect the ball off of the top of the screen
if(bally == 0) {
balldown = 1;
}
//Reflect the ball off of the bottom of the screen
if(bally + ballsize == 63) {
balldown = -1;
}
//Draw the player's paddle
arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
//If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
if(arduboy.pressed(UP_BUTTON) and playery > 0) {
playery = playery - 1;
}
//If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
if(arduboy.pressed(DOWN_BUTTON) and playery + paddleheight < 63) {
playery = playery + 1;
}
//Draw the computer's paddle
arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
if(ballx > 115 or rand() % 20 == 1) {
//If the ball is higher than the computer's paddle, move the computer's paddle up
if(bally < computery) {
computery = computery - 1;
}
//If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
}
//If the ball moves off of the screen to the left...
if(ballx < -10) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the computer a point
computerscore = computerscore + 1;
}
//If the ball moves off of the screen to the right....
if(ballx > 130) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the player a point
playerscore = playerscore + 1;
}
//If the ball makes contact with the player's paddle, bounce it back to the right
if(ballx == playerx + paddlewidth and playery < bally + ballsize and playery + paddleheight > bally) {
ballright = 1;
}
//If the ball makes contact with the computer's paddle, bounce it back to the left
if(ballx + ballsize == computerx and computery < bally + ballsize and computery + paddleheight > bally) {
ballright = -1;
}
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 2;
}
break;
case 2:
//Win screen
arduboy.setCursor(0, 0);
arduboy.print("Win Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 3;
}
break;
case 3:
//Game over screen
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 0;
}
break;
}
//Check if the button is being held down
if(arduboy.notPressed(A_BUTTON)) {
justpressed = 0;
}
arduboy.display();
}
Awesome, huh? It’s pretty much a finished game at this point! We just need to clean it up, some!
One thing we need to do is check if someone wins! Let’s do a check to see if someone has 5 points. If they do, then change the gamestate
variable to the appropriate value.
if(playerscore == 5) {
gamestate = 2;
}
if(computerscore == 5) {
gamestate = 3;
}
Hmm… Maybe we should change the code inside of case 2
for pushing the A button. Instead of taking you to gamestate = 3
, maybe we should use gamestate = 0
so that it takes you to the title screen of the game. Why would we want to show the win screen, then game over screen right afterwards, right?
Here’s the completed code so far:
//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone
#include <Arduboy.h>
Arduboy arduboy;
//Variables declared here
int gamestate = 0;
int justpressed = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;
void setup() {
arduboy.begin();
//Seed the random number generator
srand(7/8);
//Set the game to 60 frames per second
arduboy.setFrameRate(60);
arduboy.clear();
}
void loop() {
//Prevent the Arduboy from running too fast
if(!arduboy.nextFrame()) {
return;
}
arduboy.clear();
//Game code here
switch( gamestate ) {
case 0:
//Title screen
arduboy.setCursor(0, 0);
arduboy.print("Title Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 1;
}
break;
case 1:
//Gameplay screen
//Display the player's score
arduboy.setCursor(20, 0);
arduboy.print(playerscore);
//Display the computer's score
arduboy.setCursor(101, 0);
arduboy.print(computerscore);
//Draw the ball
arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
//Move the ball right
if(ballright == 1) {
ballx = ballx + 1;
}
//Move the ball left
if(ballright == -1) {
ballx = ballx - 1;
}
//Move the ball down
if(balldown == 1) {
bally = bally + 1;
}
//Move the ball up
if(balldown == -1) {
bally = bally - 1;
}
//Reflect the ball off of the top of the screen
if(bally == 0) {
balldown = 1;
}
//Reflect the ball off of the bottom of the screen
if(bally + ballsize == 63) {
balldown = -1;
}
//Draw the player's paddle
arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
//If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
if(arduboy.pressed(UP_BUTTON) and playery > 0) {
playery = playery - 1;
}
//If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
if(arduboy.pressed(DOWN_BUTTON) and playery + paddleheight < 63) {
playery = playery + 1;
}
//Draw the computer's paddle
arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
if(ballx > 115 or rand() % 20 == 1) {
//If the ball is higher than the computer's paddle, move the computer's paddle up
if(bally < computery) {
computery = computery - 1;
}
//If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
}
//If the ball moves off of the screen to the left...
if(ballx < -10) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the computer a point
computerscore = computerscore + 1;
}
//If the ball moves off of the screen to the right....
if(ballx > 130) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the player a point
playerscore = playerscore + 1;
}
//Check if the player wins
if(playerscore == 5) {
gamestate = 2;
}
//Check if the computer wins
if(computerscore == 5) {
gamestate = 3;
}
//If the ball makes contact with the player's paddle, bounce it back to the right
if(ballx == playerx + paddlewidth and playery < bally + ballsize and playery + paddleheight > bally) {
ballright = 1;
}
//If the ball makes contact with the computer's paddle, bounce it back to the left
if(ballx + ballsize == computerx and computery < bally + ballsize and computery + paddleheight > bally) {
ballright = -1;
}
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 2;
}
break;
case 2:
//Win screen
arduboy.setCursor(0, 0);
arduboy.print("Win Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 0;
}
break;
case 3:
//Game over screen
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 0;
}
break;
}
//Check if the button is being held down
if(arduboy.notPressed(A_BUTTON)) {
justpressed = 0;
}
arduboy.display();
}
9. Reset Function
If you tested this out, you’re going to notice that once you win and try to play again, you won’t really be able to. The reason this happens is that the Arduboy remembers the score from the last match! It starts the new match of Pong, checks the scores, and realizes someone has already won, then shows you either the win screen or game over screen.
What we need to do is reset the game’s variables… ballx
, playerscore
, and computerscore
.
Scroll all the way down to case 2
and case 3
. Inside of both of them, there’s a check to see if we push the A button. This is a great place to put the code to reset our variables since pushing A is supposed to reset the game to the very beginning.
So, inside of the button checks of case 2
and case 3
, put this code:
ballx = 63;
playerscore = 0;
computerscore = 0;
Actually! I got an idea! Instead of writing the same code multiple times, I should teach you about writing functions to save you time!
You already kinda know what functions are, right? They’re instructions that the computer follows. One example of this is arduboy.clear()
, which will erase everything on the screen!
Well, one cool thing about programming is that you can write your own functions! It’s really helpful! Sometimes, you’ll have code that you want to use over and over again, but maybe you don’t want to type over and over again…
Scroll all the way up… After you declare your variables, let’s declare a function. We’ll call it
resetgame()
. Type this out:
void resetgame() {
}
Inside of the braces, we’ll be able to put the instructions that are contained within the resetgame()
function. Let’s update it with the code we used a moment ago!
void resetgame() {
ballx = 63;
playerscore = 0;
computerscore = 0;
}
There we go! Any time we want to run those instructions, instead of typing out all 3 of those lines, we can save time by typing resetgame()
instead!
Back down near the bottom, add the resetgame()
function where you change gamestate
to 0!
If you got confused, here’s the full code!
//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone
#include <Arduboy.h>
Arduboy arduboy;
//Variables declared here
int gamestate = 0;
int justpressed = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;
void resetgame() {
ballx = 63;
playerscore = 0;
computerscore = 0;
}
void setup() {
arduboy.begin();
//Seed the random number generator
srand(7/8);
//Set the game to 60 frames per second
arduboy.setFrameRate(60);
arduboy.clear();
}
void loop() {
//Prevent the Arduboy from running too fast
if(!arduboy.nextFrame()) {
return;
}
arduboy.clear();
//Game code here
switch( gamestate ) {
case 0:
//Title screen
arduboy.setCursor(0, 0);
arduboy.print("Title Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 1;
}
break;
case 1:
//Gameplay screen
//Display the player's score
arduboy.setCursor(20, 0);
arduboy.print(playerscore);
//Display the computer's score
arduboy.setCursor(101, 0);
arduboy.print(computerscore);
//Draw the ball
arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
//Move the ball right
if(ballright == 1) {
ballx = ballx + 1;
}
//Move the ball left
if(ballright == -1) {
ballx = ballx - 1;
}
//Move the ball down
if(balldown == 1) {
bally = bally + 1;
}
//Move the ball up
if(balldown == -1) {
bally = bally - 1;
}
//Reflect the ball off of the top of the screen
if(bally == 0) {
balldown = 1;
}
//Reflect the ball off of the bottom of the screen
if(bally + ballsize == 63) {
balldown = -1;
}
//Draw the player's paddle
arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
//If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
if(arduboy.pressed(UP_BUTTON) and playery > 0) {
playery = playery - 1;
}
//If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
if(arduboy.pressed(DOWN_BUTTON) and playery + paddleheight < 63) {
playery = playery + 1;
}
//Draw the computer's paddle
arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
if(ballx > 115 or rand() % 20 == 1) {
//If the ball is higher than the computer's paddle, move the computer's paddle up
if(bally < computery) {
computery = computery - 1;
}
//If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
if(bally + ballsize > computery + paddleheight) {
computery = computery + 1;
}
}
//If the ball moves off of the screen to the left...
if(ballx < -10) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the computer a point
computerscore = computerscore + 1;
}
//If the ball moves off of the screen to the right....
if(ballx > 130) {
//Move the ball back to the middle of the screen
ballx = 63;
//Give the player a point
playerscore = playerscore + 1;
}
//Check if the player wins
if(playerscore == 5) {
gamestate = 2;
}
//Check if the computer wins
if(computerscore == 5) {
gamestate = 3;
}
//If the ball makes contact with the player's paddle, bounce it back to the right
if(ballx == playerx + paddlewidth and playery < bally + ballsize and playery + paddleheight > bally) {
ballright = 1;
}
//If the ball makes contact with the computer's paddle, bounce it back to the left
if(ballx + ballsize == computerx and computery < bally + ballsize and computery + paddleheight > bally) {
ballright = -1;
}
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
gamestate = 2;
}
break;
case 2:
//Win screen
arduboy.setCursor(0, 0);
arduboy.print("Win Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
resetgame();
gamestate = 0;
}
break;
case 3:
//Game over screen
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen");
//Change the gamestate
if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
justpressed = 1;
resetgame();
gamestate = 0;
}
break;
}
//Check if the button is being held down
if(arduboy.notPressed(A_BUTTON)) {
justpressed = 0;
}
arduboy.display();
}
Wow!
Give yourself a pat on the back! Good job with this one! It was a long tutorial, but if you made it this far, you really deserve some kind of prize! You are definitely ready to start experimenting and making your own game!
Next Tutorial
The next tutorial will be a bit shorter. I’ll teach you some cool stuff that I purposely skipped before. A lot more features that will make programming a little easier for you. We’ll use that stuff to upgrade this Pong game to have more features, too!
This tutorial is copied and abridged from the original by Jonathan Holmes.
Credits
I wrote this tutorial in order to give back to the programming community that taught me to get into it about 10 years ago. If you’d like to follow me on Twitter, please do so at http://www.twitter.com/crait34 . I’d greatly appreciate that.
Make Your Own Arduboy Game – Graphics!
In this tutorial, I’m going to show you how to make a game demo where you can move a sprite around a screen that has a tiled background. I’ll also teach you some more basic programming fundamentals.
Starting Code
Let’s use this code to start our program. Open the Arduino IDE, start a new project, and make sure the code looks like this:
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
arduboy.display();
}
Starting Images
Right-click and download these two images to your computer.
This image will be your background image. We’re going to tile it across the entire Arduboy screen.
This image will be your player sprite, which is the image that represents your character. You’ll move this face around your screen on the Arduboy.
Convert Images
Remember how I said that you can store data and variables in several formats? Remember, one format that we used before for numbers was int
. Well, to store images, we actually want to convert them into raw data that we can store them in byte arrays. Don’t worry too much about what that means, just know it’s different than an int
and can hold a different amount of data.
I’ve seen some people on this forum say that they convert images to raw data by hand, but luckily, I created an online tool that will do it for us called ToChars. Open up http://www.crait.net/tochars/615 in a new tab and follow these instructions for each image.
1) Click Start
2) Click Browse
3) Set Size
For the background image, set the height and width to both be 8 pixels. For the player sprite, set them to be 16 pixels.
4) Click Convert
5) Grab Data
Here’s the raw data for background image:
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
Here’s the raw data for the player sprite:
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
Hold onto these for a second and I’ll teach you where to put them.
Talking About Initializing Variables
Alright, I kinda left out some information about initializing variables. So far, I’ve had you initialize variables at the top of the sketch, right under the Arduboy arduboy;
line. But, it’s important to know that you can actually initialize them pretty much anywhere. We’ll do that later on, so I don’t want it to surprise you.
I want to teach you a shortcut, though. When we’ve been initializing variables, we have been doing them in the following format:
int counter;
counter = 5;
This is valid, but there’s a shortcut that you can take. You can actually set a value to a variable at the same time you initialize it.
int counter = 5;
Cool, huh? You can also do this with constants.
Constants
A constant is like a variable in the sense that you can have it store data that you can also initialize. The only difference is that the data can’t be changed. It saves memory to store data in constants instead of variables if you aren’t going going to change the data that it holds. We’ll do that with our image data since we won’t change the images at all.
To create a constant, you use the const
keyword in front of the initialization.
int counter = 5;
const int height = 5;
Since you can’t change the data inside of a constant, height
cannot be changed or given a new value. If you were to use the following code, you would get an error and the code will not work.
height = 12;
Storing Images
Whew! That was a lot to tell you real quick, but let’s move on to storing the raw image data into byte arrays. Where you normally initialize variables, put the following code:
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
To summarize this code, we converted 2 images to 2 groups of characters. One is named background
and the other is named player
. Let’s break this code down a little and go word-by-word.
const
: We already know that we are creating a variable that cannot be changed.unsigned
: This means that we cannot use negative values. Just ignore this for a while.char
: The way we store byte data is by putting it into achar
, which stands for character. This is the kind of variable that we are creating instead of using anint
like the last tutorial.background
: Like all variables, we need to give these two character arrays names. The top one is calledbackground
and the bottom one is calledplayer
.[]
: The brackets that you see here means that we’re creating an array. An array is a group of variables. We’re creating groups ofchar
variables. We can call them character arrays.=
: Like in the shortcut that I explained above, when you initialize a variable, you can assign a value to it at the same time. We can do this with arrays. We’ll actually assign the characters directly into the array.{ }
: Anything inside of the braces is what we’re actually putting inside of the array.0x84, 0x20,...
: This is the data that we got from the ToChars site. It’s an image stored in multiple bytes/characters. Each character is separated by a comma. ToChars converted the images to characters. Get it?
Your game’s code should look like this:
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
arduboy.display();
}
Parameters
I can’t wait to show you how to draw the above images to the Arduboy screen! But I can’t just yet! I need to tell you about a function’s parameters! Remember that a function is an instruction for the computer. Sometimes, they look like this:
arduboy.clear();
That clears the Arduboy’s screen. It’s a function that is easy to understand. Here’s another:
arduboy.print(randomnumber);
This is from the last tutorial. The randomnumber
is a variable that we put into the function. We told the Arduboy what to print. That’s called a parameter. And in fact, functions can have multiple parameters. We already saw one earlier that did this in the last tutorial, too!
arduboy.setCursor(0, 0);
The arduboy.setCursor()
function requires two parameters that are separated by a comma.
The function to draw an image to the Arduboy’s screen has a lot more parameters that you need to use. This is actually pretty common.
Drawing Images
So, we want to draw an image to the Arduboy screen, finally! Let’s start by drawing the player sprite that we called player
. Inside of the loop()
area, after we clear the screen, let’s add this line of code:
arduboy.drawBitmap(5, 10, player, 16, 16, WHITE);
- The first two parameters are
5
and10
. These numbers represent the X and Y location that the image will be drawn to. Changing these numbers will change where the image appears. - The next parameter that’s given is the image that we want to draw, which is
player
. - The next two parameters are the width and height of the image that is being drawn. For the
player
image, we’ll have16, 16
, but for the background image, we’ll have8, 8
since that’s how big those images are. - the last parameter is what color we want to draw to the screen.
WHITE
means that we’re drawing all the white pixels in the image as white.BLACK
means that we’re drawing all the white pixels in the image as black. It seems counter-intuitive, but it becomes useful later on.
Okay! If your code looks like the following, then go ahead and put it on your Arduboy!
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
arduboy.drawBitmap(5, 10, player, 16, 16, WHITE);
arduboy.display();
}
Your Arduboy should properly display as:
Control The Sprite
Okay, remember how we controlled the value of a variable with the Up and Down buttons? To move the sprite around, we’ll do something very similar.
To start, let’s initialize 2 variables at the top of the sketch:
int playerx = 5;
int playery = 10;
The playerx
and playery
variables will hold the coordinates for our image as it’s moving around. In the arduboy.drawBitmap()
function, let’s replace the X and Y parameter with these variables.
arduboy.drawBitmap(palyerx, playery, player, 16, 16, WHITE);
Let’s increase/decrease those variables’ values when the Up/Down and Left/Right buttons are pressed.
if(arduboy.pressed(LEFT_BUTTON)) {
playerx = palyerx - 1;
}
if(arduboy.pressed(RIGHT_BUTTON)) {
playerx = palyerx + 1;
}
if(arduboy.pressed(UP_BUTTON)) {
playery = palyery - 1;
}
if(arduboy.pressed(DOWN_BUTTON)) {
playery = palyery + 1;
}
That’s all it takes to move an image around the screen! Test out the code on your Arduboy and have fun with it!
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
int playerx = 5;
int playery = 10;
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
if(arduboy.pressed(LEFT_BUTTON)) {
playerx = playerx - 1;
}
if(arduboy.pressed(RIGHT_BUTTON)) {
playerx = playerx + 1;
}
if(arduboy.pressed(UP_BUTTON)) {
playery = playery - 1;
}
if(arduboy.pressed(DOWN_BUTTON)) {
playery = playery + 1;
}
arduboy.drawBitmap(playerx, playery, player, 16, 16, WHITE);
arduboy.display();
}
For Loop
Okay, remember how I said that loops tell the computer to repeat a set of actions over and over again? I’m going to teach you about one called the for loop. This loop allows you to repeat a set of instructions for a specified amount of times.
Why is this important? We want to make a background for this game, but the background
image that we have is too small to fill the entire screen, so we’ll print it many times in different places until it fills up the entire screen.
Let’s say we want to use the arduboy.print();
function to print your name 10 times in a row. We’d have to use a loop keyword and we’ll need a variable to keep track of how many times we’ve printed so far, and a line of code to increase that variable every time we loop through. Luckily, the for loop has a lot of that built-in. Take a look at it, below:
for( int counter = 0; counter < 10; counter = counter + 1 ) {
arduboy.print("crait");
}
Alright, again, let’s break this down:
for
: This is kinda like a function. It lets the Arduboy know you want to use a for loop.int counter = 0;
: Whenever you run afor
loop, you’ll need to initialize/set a variable. We create acounter
variable that is equal to 0. This code gets executed before the loop is entered.counter < 10;
: This is the check to see if the loop should run or not. Ifcounter
is less than10
, then it should run.counter = counter + 1
: After all of the instructions inside of the loop are followed, this code is ran. It increasescounter
by1
. Eventually,counter
will grow big enough so that the loop does not execute anymore.{ }
: Anything inside of these braces will be considered part of the loop and only be executed when the Arduboy is running thisfor
loop.
Instead of this code printing my name 10 times, we can have it count!
for( int counter = 0; counter < 10; counter = counter + 1 ) {
arduboy.print( counter );
}
If we run the above loop, the Arduboy would print out the numbers 0 to 9! You can change the number of times it will loop, you can change what number the counter
starts with, and you can even change how many numbers counter
is increased by every time the loop is run.
Tile Background
Alrighty! Let’s get the background implemented! We’re almost done!
The Arduboy’s screen resolution is 128 pixels wide by 64 pixels tall, which means if we use a background image of 8 pixels by 8 pixels, we would have 16 columns wide by 8 rows high.
Let’s tile the background image 8 times across the top of the screen.
for( int backgroundx = 0; backgroundx < 128; backgroundx = backgroundx + 8 ) {
arduboy.drawBitmap( backgroundx, 0, background, 8, 8, WHITE );
}
Do you understand what’s going on, here? Notice that the
backgroundx
variable is used to count the loop, but also used when drawing the background image. Since we want to span the background across the width of the screen, we want to count up to 128. Because we want to put a new image every 8 pixels, we’ll increase it by 8 every time a tile is drawn.
Add this code before you draw your sprite and run it on your Arduboy. It should appear like this:
Next, let’s add another for
loop. Instead of doing this after the previous loop, we’ll have this loop inside of the other!
for( int backgroundx = 0; backgroundx < 128; backgroundx = backgroundx + 8 ) {
for( int backgroundy = 0; backgroundy < 64; backgroundy = backgroundy + 8 ) {
arduboy.drawBitmap( backgroundx, backgroundy, background, 8, 8, WHITE );
}
}
To summarize the above code, for each column on the screen, we will draw several rows of background
images until the screen is full. This is the result:
Here’s the full code:
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
int playerx = 5;
int playery = 10;
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
if(arduboy.pressed(LEFT_BUTTON)) {
playerx = playerx - 1;
}
if(arduboy.pressed(RIGHT_BUTTON)) {
playerx = playerx + 1;
}
if(arduboy.pressed(UP_BUTTON)) {
playery = playery - 1;
}
if(arduboy.pressed(DOWN_BUTTON)) {
playery = playery + 1;
}
//For each column on the screen
for( int backgroundx = 0; backgroundx < 128; backgroundx = backgroundx + 8 ) {
//For each row in the column
for( int backgroundy = 0; backgroundy < 64; backgroundy = backgroundy + 8 ) {
//Draw a background tile
arduboy.drawBitmap( backgroundx, backgroundy, background, 8, 8, WHITE );
}
}
//Draw player sprite
arduboy.drawBitmap(playerx, playery, player, 16, 16, WHITE);
arduboy.display();
}
Clean Up
Uh, oh! You notice the problem? Our character’s face is clear and you can see the background though it. How can we fix that? Let’s just simply draw a black square behind it to block the background images. Put this before we draw the player’s sprite.
arduboy.fillRect(playerx, playery, 16, 16, BLACK);
Finished Code
Here it is! Run this code on your Arduboy to make sure it works properly! If it does, then feel free to modify this code to start making your own game!
//Jonathan Holmes (crait)
//November 1st, 2016
//Moving Character Demo
#include <Arduboy.h>
Arduboy arduboy;
int playerx = 5;
int playery = 10;
const unsigned char background[] PROGMEM = {
0x84, 0x20, 0x9, 0x00, 0x24, 0x00, 0x10, 0x80,
};
const unsigned char player[] PROGMEM = {
0xfe, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0x1, 0xc1, 0x1, 0x3d, 0x25, 0x25, 0x3d, 0x1, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f,
};
void setup() {
arduboy.begin();
arduboy.clear();
}
void loop() {
arduboy.clear();
if(arduboy.pressed(LEFT_BUTTON)) {
playerx = playerx - 1;
}
if(arduboy.pressed(RIGHT_BUTTON)) {
playerx = playerx + 1;
}
if(arduboy.pressed(UP_BUTTON)) {
playery = playery - 1;
}
if(arduboy.pressed(DOWN_BUTTON)) {
playery = playery + 1;
}
//For each column on the screen
for( int backgroundx = 0; backgroundx < 128; backgroundx = backgroundx + 8 ) {
//For each row in the column
for( int backgroundy = 0; backgroundy < 64; backgroundy = backgroundy + 8 ) {
//Draw a background tile
arduboy.drawBitmap( backgroundx, backgroundy, background, 8, 8, WHITE );
}
}
//Draw black square
arduboy.fillRect(playerx, playery, 16, 16, BLACK);
//Draw player sprite
arduboy.drawBitmap(playerx, playery, player, 16, 16, WHITE);
arduboy.display();
}
What’s Next?
What’s next? I’ll teach you how to make your own Pong game from scratch!! Click here to go check it out!
This tutorial is copied and abridged from the original by Jonathan Holmes.
Credits
I wrote this tutorial in order to give back to the programming community that taught me to get into it about 10 years ago. If you’d like to follow me on Twitter, please do so at http://www.twitter.com/crait30 . I’d greatly appreciate that.