Advertisement
Guest User

Gyro Pong game box

a guest
Dec 16th, 2016
423
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 25.72 KB | None | 0 0
  1. /* Arduino UNO with MPU-6050 over I2C (GY-521 module)
  2.  *  Tom Tobback Nov 2016 - BuffaloLabs
  3.  *  built in box, Atmega328 16MHz on perf board, Duemilanove board type
  4.  *
  5.  *  PONG GAME:
  6.  *  2 players, 5 points
  7.  *
  8.  *  GYRO GAME:
  9.  *  -tilt the sensor to move the ball around
  10.  *  -catch the squares
  11.  *  -do not touch the borders
  12.  *  -catch as many as you can in 1min
  13.  *
  14.  *  GY-521 module
  15.  *  5V
  16.  *  GND
  17.  *  SDA=A4
  18.  *  SCL=A5
  19.  *  
  20.  *  1.8'' TFT LCD
  21.  *   LED 220ohm resistor to 5V
  22.  *   SCK D13
  23.  *   SDA D11
  24.  *   A0/DC D9
  25.  *   RESET D8
  26.  *   CS D10
  27.  *   GND
  28.  *   Vcc 5V
  29.  *
  30.  *  gyro/pong switch on D7
  31.  *  potentiometers on A0 and A1
  32.  *  buzzer on D3
  33.  *  LED on D2
  34.  *  high score in EEPROM
  35.  *  button on RST
  36.  *
  37.  *  IMU code:
  38.  *  Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
  39.  This software may be distributed and modified under the terms of the GNU
  40.  General Public License version 2 (GPL2) as published by the Free Software
  41.  Foundation and appearing in the file GPL2.TXT included in the packaging of
  42.  this file. Please note that GPL2 Section 2[b] requires that all works based
  43.  on this software must also be made publicly available under the terms of
  44.  the GPL2 ("Copyleft").
  45.  Contact information
  46.  -------------------
  47.  Kristian Lauszus, TKJ Electronics
  48.  Web      :  http://www.tkjelectronics.com
  49.  e-mail   :  kristianl@tkjelectronics.com
  50.  */
  51.  
  52. #include <EEPROM.h>
  53.  
  54. #include <TFT.h>  // Arduino LCD library
  55. #include <SPI.h>
  56.  
  57. #define cs   10
  58. #define dc   9
  59. #define rst  8
  60.  
  61. TFT TFTscreen = TFT(cs, dc, rst);
  62.  
  63. int tftWidth;
  64. int tftHeight;
  65.  
  66. ////////////////////// GYRO ////////////////////////////////////////////////////
  67.  
  68. #include <Wire.h>
  69. #include "Kalman.h" // Source: https://github.com/TKJElectronics/KalmanFilter
  70.  
  71. #define RESTRICT_PITCH // Comment out to restrict roll to ±90deg instead - please read: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf
  72.  
  73. Kalman kalmanX; // Create the Kalman instances
  74. Kalman kalmanY;
  75.  
  76. /* IMU Data */
  77. double accX, accY, accZ;
  78. double gyroX, gyroY, gyroZ;
  79. int16_t tempRaw;
  80.  
  81. double gyroXangle, gyroYangle; // Angle calculate using the gyro only
  82. double compAngleX, compAngleY; // Calculated angle using a complementary filter
  83. double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter
  84.  
  85. float roll;
  86. float pitch;
  87. float posX;
  88. float posY;
  89. int targetX;
  90. int targetY;
  91. int success_counter = 0;
  92. long start_time;
  93. boolean gyro;         // switch gyro/pong at startup
  94. int sec = 60;
  95. int high_score;
  96. int time_left;
  97.  
  98. uint32_t timer;
  99. uint8_t i2cData[14]; // Buffer for I2C data
  100.  
  101. /////////////////////////////// PONG //////////////////////////////////////////////
  102.  
  103. const float pi = 3.14159265;
  104. int player1 = -1;
  105. int player2 = -1;
  106. int player1_old;
  107. int player2_old;
  108. float ball_x = 64;
  109. float ball_y = 32;
  110. float ball_x_old;
  111. float ball_y_old;
  112. float ball_angle;  // in radians 0 -> 2pi
  113. int speed = 2;      // normal = 2
  114. int counter = 0;
  115. int score1 = 0;
  116. int score2 = 0;
  117.  
  118. long time;
  119. long interval = 5000;
  120.  
  121. ////////////////////////////////////////////////////////////////////////////////////////
  122. ////////////////////////////////////////////////////////////////////////////////////////
  123. ////////////////////////////////////////////////////////////////////////////////////////
  124.  
  125. void setup() {
  126.  
  127.   if (EEPROM.read(0) == 255) {
  128.     EEPROM.write(0, 0);            // reset GYRO high score to 0
  129.   }
  130.  
  131.   TFTscreen.begin();
  132.   tftWidth = TFTscreen.width();
  133.   tftHeight = TFTscreen.height();
  134.   TFTscreen.background(0, 0, 0);
  135.  
  136.   pinMode(2, OUTPUT);             // LED
  137.   pinMode(3, OUTPUT);             // buzzer
  138.   pinMode(6, INPUT_PULLUP);       // button restart
  139.  
  140.   digitalWrite(2, HIGH);
  141.   tone(3, 500);
  142.   delay(100);
  143.   tone(3, 1000);
  144.   delay(100);
  145.   noTone(3);
  146.   digitalWrite(2, LOW);
  147.  
  148.   pinMode(7, INPUT_PULLUP);       // gyro or pong
  149.   gyro = digitalRead(7);
  150.  
  151.   Serial.begin(115200);
  152.   Serial.println(tftWidth);
  153.   Serial.println(tftHeight);
  154.   if (gyro) {
  155.     initGyro();
  156.     high_score = EEPROM.read(0);
  157.     time_left = sec;
  158.     randomSeed(analogRead(0));
  159.     setRandom();
  160.     drawIntroGyro();
  161.   }
  162.   else {
  163.     drawIntroPong();
  164.     ball_angle = float(random(100, 200)) / 100;
  165.   }
  166.  
  167.   delay(2000);
  168.   TFTscreen.background(0, 0, 0);                      // black    -- clear screen
  169.  
  170.   if (gyro) {
  171.     drawGyroStart();                                // box
  172.     drawGyroScore(0);
  173.     posX = tftWidth / 2;
  174.     posY = tftHeight / 2;
  175.   }
  176.   else {
  177.     drawPongScores(0, 0);                            // PONG initial scores
  178.   }
  179.  
  180.   timer = micros();
  181.   start_time = millis();
  182. }
  183.  
  184. ////////////////////////////////////////////////////////////////////////////////////////
  185. ////////////////////////////////////////////////////////////////////////////////////////
  186.  
  187. void loop() {
  188.  
  189.   if (gyro) {
  190.     getGyro();      // this takes about 2msec
  191.     pitch = - pitch;  // depending on orientation of sensor
  192.     roll = - roll;
  193.     drawGyro();
  194.   }
  195.   else {
  196.     player1_old = player1;
  197.     player2_old = player2;
  198.     player1 = constrain(map(analogRead(A0), 200, 500, tftHeight - 25, 0), 0, tftHeight - 25 ); // potentiometer on pin A0
  199.     player2 = constrain(map(analogRead(A1), 200, 500, tftHeight - 25, 0), 0, tftHeight - 25 ); // potentiometer on pin A1
  200.     drawPong();
  201.   }
  202. }
  203.  
  204. ////////////////////////////////////////////////////////////////////////////////////////
  205. ////////////////////////////////////////////////////////////////////////////////////////
  206. ////////////////////////////////  GYRO  /////////////////////////////////////////////////
  207. ////////////////////////////////////////////////////////////////////////////////////////
  208. ////////////////////////////////////////////////////////////////////////////////////////
  209.  
  210. void initGyro() {
  211.   Wire.begin();
  212.   TWBR = ((F_CPU / 400000L) - 16) / 2; // Set I2C frequency to 400kHz
  213.  
  214.   i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
  215.   i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
  216.   i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
  217.   i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
  218.   while (i2cWrite(0x19, i2cData, 4, false)); // Write to all four registers at once
  219.   while (i2cWrite(0x6B, 0x01, true)); // PLL with X axis gyroscope reference and disable sleep mode
  220.  
  221.   while (i2cRead(0x75, i2cData, 1));
  222.   if (i2cData[0] != 0x68) { // Read "WHO_AM_I" register
  223.     Serial.print(F("Error reading sensor"));
  224.     while (1);
  225.   }
  226.  
  227.   delay(100); // Wait for sensor to stabilize
  228.  
  229.   /* Set kalman and gyro starting angle */
  230.   while (i2cRead(0x3B, i2cData, 6));
  231.   accX = (i2cData[0] << 8) | i2cData[1];
  232.   accY = (i2cData[2] << 8) | i2cData[3];
  233.   accZ = (i2cData[4] << 8) | i2cData[5];
  234.  
  235.   // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  236.   // atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
  237.   // It is then converted from radians to degrees
  238. #ifdef RESTRICT_PITCH // Eq. 25 and 26
  239.   roll  = atan2(accY, accZ) * RAD_TO_DEG;
  240.   pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
  241. #else // Eq. 28 and 29
  242.   roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  243.   pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  244. #endif
  245.  
  246.   kalmanX.setAngle(roll); // Set starting angle
  247.   kalmanY.setAngle(pitch);
  248.   gyroXangle = roll;
  249.   gyroYangle = pitch;
  250.   compAngleX = roll;
  251.   compAngleY = pitch;
  252. }
  253.  
  254. ///////////////////////////////////////////////////////////////////////////////////////////////////
  255. ///////////////////////////////////////////////////////////////////////////////////////////////////
  256.  
  257. void getGyro() {
  258.   /* Update all the values */
  259.   while (i2cRead(0x3B, i2cData, 14));
  260.   accX = ((i2cData[0] << 8) | i2cData[1]);
  261.   accY = ((i2cData[2] << 8) | i2cData[3]);
  262.   accZ = ((i2cData[4] << 8) | i2cData[5]);
  263.   tempRaw = (i2cData[6] << 8) | i2cData[7];
  264.   gyroX = (i2cData[8] << 8) | i2cData[9];
  265.   gyroY = (i2cData[10] << 8) | i2cData[11];
  266.   gyroZ = (i2cData[12] << 8) | i2cData[13];
  267.  
  268.   double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
  269.   timer = micros();
  270.  
  271.   // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  272.   // atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
  273.   // It is then converted from radians to degrees
  274. #ifdef RESTRICT_PITCH // Eq. 25 and 26
  275.   roll  = atan2(accY, accZ) * RAD_TO_DEG;
  276.   pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
  277. #else // Eq. 28 and 29
  278.   roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  279.   pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  280. #endif
  281.  
  282.   double gyroXrate = gyroX / 131.0; // Convert to deg/s
  283.   double gyroYrate = gyroY / 131.0; // Convert to deg/s
  284.  
  285. #ifdef RESTRICT_PITCH
  286.   // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  287.   if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) {
  288.     kalmanX.setAngle(roll);
  289.     compAngleX = roll;
  290.     kalAngleX = roll;
  291.     gyroXangle = roll;
  292.   } else
  293.     kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
  294.  
  295.   if (abs(kalAngleX) > 90)
  296.     gyroYrate = -gyroYrate; // Invert rate, so it fits the restriced accelerometer reading
  297.   kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);
  298. #else
  299.   // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  300.   if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {
  301.     kalmanY.setAngle(pitch);
  302.     compAngleY = pitch;
  303.     kalAngleY = pitch;
  304.     gyroYangle = pitch;
  305.   } else
  306.     kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter
  307.  
  308.   if (abs(kalAngleY) > 90)
  309.     gyroXrate = -gyroXrate; // Invert rate, so it fits the restriced accelerometer reading
  310.   kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
  311. #endif
  312.  
  313.   gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter
  314.   gyroYangle += gyroYrate * dt;
  315.   //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  316.   //gyroYangle += kalmanY.getRate() * dt;
  317.  
  318.   compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter
  319.   compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;
  320.  
  321.   // Reset the gyro angle when it has drifted too much
  322.   if (gyroXangle < -180 || gyroXangle > 180)
  323.     gyroXangle = kalAngleX;
  324.   if (gyroYangle < -180 || gyroYangle > 180)
  325.     gyroYangle = kalAngleY;
  326.  
  327.   /* Print Data */
  328. #if 0 // Set to 1 to activate
  329.   Serial.print(accX); Serial.print("\t");
  330.   Serial.print(accY); Serial.print("\t");
  331.   Serial.print(accZ); Serial.print("\t");
  332.  
  333.   Serial.print(gyroX); Serial.print("\t");
  334.   Serial.print(gyroY); Serial.print("\t");
  335.   Serial.print(gyroZ); Serial.print("\t");
  336.  
  337.   Serial.print("\t");
  338.  
  339.   Serial.print(roll); Serial.print("\t");
  340.   Serial.print(gyroXangle); Serial.print("\t");
  341.   Serial.print(compAngleX); Serial.print("\t");
  342.   Serial.print(kalAngleX); Serial.print("\t");
  343.  
  344.   Serial.print("\t");
  345.  
  346.   Serial.print(pitch); Serial.print("\t");
  347.   Serial.print(gyroYangle); Serial.print("\t");
  348.   Serial.print(compAngleY); Serial.print("\t");
  349.   Serial.print(kalAngleY); Serial.print("\t");
  350.  
  351.   Serial.print("\t");
  352.  
  353.   double temperature = (double)tempRaw / 340.0 + 36.53;
  354.   Serial.print(temperature); Serial.print("\t");
  355.   Serial.println();
  356. #endif
  357. }
  358.  
  359. ////////////////////////////////////////////////////////////////////////////////////////
  360. ////////////////////////////////////////////////////////////////////////////////////////
  361.  
  362. void drawGyro() {
  363.  
  364.   TFTscreen.noStroke();
  365.  
  366.   // erase ball
  367.   TFTscreen.fill(0, 0, 0);                          // black
  368.   TFTscreen.circle(posX, posY, 3);
  369.  
  370.   posX -= roll / 10.0;
  371.   posY += pitch / 10.0;
  372.   posX = constrain(posX, 32, tftWidth);
  373.   posY = constrain(posY, 1, tftHeight);
  374.  
  375.   // draw ball
  376.   TFTscreen.fill(0, 255, 0);                          // green
  377.   TFTscreen.circle(posX, posY, 3);
  378.  
  379.   int current_time_left = abs(sec - (millis() - start_time) / 1000.0);
  380.   if (current_time_left < time_left) {
  381.     // erase time
  382.     char timer[4];
  383.     String str_timer = String(time_left);
  384.     str_timer.toCharArray(timer, 4);
  385.     TFTscreen.stroke(0, 0, 0);                    // black
  386.     TFTscreen.text(timer, 5, 100);
  387.     // draw time
  388.     str_timer = String(current_time_left);
  389.     str_timer.toCharArray(timer, 4);
  390.     TFTscreen.stroke(255, 255, 255);                    // white
  391.     TFTscreen.text(timer, 5, 100);
  392.     time_left = current_time_left;
  393.   }
  394.  
  395.   if (posX > (targetX - 4) && posX < (targetX + 4) && posY > (targetY - 4) && posY < (targetY + 4)) {   // CATCH
  396.     success_counter++;
  397.     digitalWrite(2, HIGH);
  398.     drawGyroScore(success_counter);
  399.     TFTscreen.noStroke();
  400.     // erase target
  401.     TFTscreen.fill(0, 0, 0);                          // black
  402.     TFTscreen.rect(targetX - 3, targetY - 3, 6, 6);     // target
  403.     setRandom();
  404.     // draw target
  405.     TFTscreen.fill(0, 0, 255);                          // red
  406.     TFTscreen.rect(targetX - 3, targetY - 3, 6, 6);     // target
  407.     tone(3, 1000, 100);
  408.   }
  409.   else {
  410.     digitalWrite(2, LOW);
  411.   }
  412.  
  413.   if (posX < 36 || posX > tftWidth - 5 || posY < 5 || posY > tftHeight - 5) {                         // HIT BORDER = GAME OVER
  414.     tone(3, 100, 1000);
  415.     TFTscreen.stroke(0, 0, 255);                    // red
  416.     TFTscreen.text("GAME OVER", 40, 40);
  417.     TFTscreen.stroke(255, 0, 0);                    // blue
  418.     TFTscreen.setTextSize(1);
  419.     TFTscreen.text("press button", 60, 70);
  420.     TFTscreen.text("to start again", 55, 90);
  421.     while (1) {                                                               // while wait for reset, short blink
  422.       digitalWrite(2, HIGH);
  423.       delay(20);
  424.       digitalWrite(2, LOW);
  425.       delay(2000);
  426.     }
  427.   }
  428.  
  429.   if ((millis() - start_time) / 1000.0 >= sec) {                                        // many SEC = GAME OVER
  430.     if (success_counter > high_score) {                                               // new high score
  431.       high_score = success_counter;
  432.       EEPROM.write(0, high_score);
  433.  
  434.       TFTscreen.background(0, 255, 0);            // green
  435.       TFTscreen.stroke(0, 0, 255);                // red
  436.       TFTscreen.text("NEW", 70, 20);
  437.       TFTscreen.text("HIGH SCORE!", 20, 40);
  438.       char score[4];
  439.       String str_score = String(high_score);
  440.       str_score.toCharArray(score, 4);
  441.       TFTscreen.text(score, 75, 60);
  442.       TFTscreen.setTextSize(1);
  443.       TFTscreen.text("congratulations!", 30, 90);
  444.       TFTscreen.text("press button to start", 20, 100);
  445.  
  446.       digitalWrite(2, HIGH);
  447.       for (int i = 1000; i < 4000; i += 5) {
  448.         tone(3, i);
  449.         delay(1);
  450.       }
  451.       for (int i = 4000; i > 1000; i -= 5) {
  452.         tone(3, i);
  453.         delay(1);
  454.       }
  455.       noTone(3);
  456.       digitalWrite(2, LOW);
  457.     }
  458.     else {                                                                            // no new high score
  459.       TFTscreen.stroke(0, 255, 0);                    // green
  460.       TFTscreen.text("GAME OVER", 40, 40);
  461.       TFTscreen.stroke(255, 0, 0);                    // blue
  462.       TFTscreen.setTextSize(1);
  463.       TFTscreen.text("press button", 60, 70);
  464.       TFTscreen.text("to start again", 55, 90);
  465.       for (int i = 0; i < 30; i++) {
  466.         tone(3, 2000);
  467.         digitalWrite(2, HIGH);
  468.         delay(20);
  469.         noTone(3);
  470.         digitalWrite(2, LOW);
  471.         delay(20);
  472.       }
  473.     }
  474.     while (1) {                                                               // while wait for reset, short blink
  475.       digitalWrite(2, HIGH);
  476.       delay(20);
  477.       digitalWrite(2, LOW);
  478.       delay(2000);
  479.     }
  480.   }
  481.   delay(10);                     // speed of GYRO game
  482. }
  483.  
  484. void setRandom() {
  485.   targetX = random(36, tftWidth - 4);  // border is 67 and 126
  486.   targetY = random(4, tftHeight - 4);    // border is 1 and 62
  487. }
  488.  
  489.  
  490. ////////////////////////////////////////////////////////////////////////////////////////
  491. ////////////////////////////////////////////////////////////////////////////////////////
  492.  
  493. void drawIntroGyro() {
  494.   TFTscreen.background(0, 0, 0);  // black
  495.   TFTscreen.stroke(255, 255, 255); // white
  496.   TFTscreen.setTextSize(2);
  497.   TFTscreen.text("BuffaloLabs", 18, 20);
  498.   TFTscreen.text("GYRO GAME", 30, 50);
  499.   TFTscreen.text("highscore:", 10, 80);
  500.   char highscore[4];
  501.   String str_high = String(high_score);
  502.   str_high.toCharArray(highscore, 4);
  503.   TFTscreen.text(highscore, 130, 80);
  504. }
  505.  
  506. ////////////////////////////////////////////////////////////////////////////////////////
  507. ////////////////////////////////////////////////////////////////////////////////////////
  508.  
  509. void drawGyroStart() {
  510.   TFTscreen.background(0, 0, 0);  // black
  511.   TFTscreen.stroke(255, 255, 255); // white
  512.   TFTscreen.rect(32, 0, tftWidth - 32, tftHeight);
  513.  
  514.   TFTscreen.noStroke();
  515.   // draw target
  516.   TFTscreen.fill(0, 0, 255);                          // red
  517.   TFTscreen.rect(targetX - 3, targetY - 3, 6, 6);     // target
  518.  
  519. }
  520.  
  521. ////////////////////////////////////////////////////////////////////////////////////////
  522. ////////////////////////////////////////////////////////////////////////////////////////
  523.  
  524. void drawGyroScore(int s) {
  525.   char score[4];
  526.   String str_score = String(s - 1);
  527.   str_score.toCharArray(score, 4);
  528.   TFTscreen.stroke(0, 0, 0);                    // black
  529.   TFTscreen.text(score, 5, 20);
  530.   str_score = String(s);
  531.   str_score.toCharArray(score, 4);
  532.   TFTscreen.stroke(255, 255, 255);                    // white
  533.   TFTscreen.text(score, 5, 20);
  534.  
  535. }
  536.  
  537. ////////////////////////////////////////////////////////////////////////////////////////
  538. ////////////////////////////////////////////////////////////////////////////////////////
  539. //////////////////////////////  PONG  //////////////////////////////////////////////////
  540. ////////////////////////////////////////////////////////////////////////////////////////
  541. ////////////////////////////////////////////////////////////////////////////////////////
  542. ////////////////////////////////////////////////////////////////////////////////////////
  543.  
  544. void drawIntroPong() {
  545.   TFTscreen.background(0, 0, 0);  // black
  546.   TFTscreen.stroke(255, 255, 255); // white
  547.   TFTscreen.setTextSize(2);
  548.   TFTscreen.text("BuffaloLabs", 18, 20);
  549.   TFTscreen.text("PONG GAME", 30, 50);
  550.   TFTscreen.text("2 players ", 30, 80);
  551. }
  552.  
  553. ////////////////////////////////////////////////////////////////////////////////////////
  554. ////////////////////////////////////////////////////////////////////////////////////////
  555.  
  556. void drawPongScores(int s1, int s2) {
  557.  
  558.   TFTscreen.setTextSize(2);
  559.   TFTscreen.stroke(0, 0, 0);                    // black
  560.   char score[4];
  561.   String str_score = String(s1 - 1);
  562.   str_score.toCharArray(score, 4);
  563.   TFTscreen.text(score, 20, 100);
  564.   str_score = String(s2 - 1);
  565.   str_score.toCharArray(score, 4);
  566.   TFTscreen.text(score, 130, 100);
  567.  
  568.   TFTscreen.stroke(255, 255, 255);                    // white
  569.   str_score = String(s1);
  570.   str_score.toCharArray(score, 4);
  571.   TFTscreen.text(score, 20, 100);
  572.   TFTscreen.stroke(255, 255, 255);
  573.   str_score = String(s2);
  574.   str_score.toCharArray(score, 4);
  575.   TFTscreen.text(score, 130, 100);
  576.  
  577.   delay(1000);
  578.   TFTscreen.stroke(0, 0, 0);                    // black
  579.   str_score = String(s1);
  580.   str_score.toCharArray(score, 4);
  581.   TFTscreen.text(score, 20, 100);
  582.   str_score = String(s2);
  583.   str_score.toCharArray(score, 4);
  584.   TFTscreen.text(score, 130, 100);
  585.  
  586.   TFTscreen.noStroke();               // better speed without stroke
  587.  
  588. }
  589.  
  590. ////////////////////////////////////////////////////////////////////////////////////////
  591. ////////////////////////////////////////////////////////////////////////////////////////
  592.  
  593. void drawPong() {
  594.  
  595.   unsigned long timestamp = millis();
  596.   // erase ball
  597.   TFTscreen.fill(0, 0, 0);                          // black
  598.   TFTscreen.circle(ball_x + 0.5, ball_y + 0.5, 3);      // for rounding instead of truncating
  599.  
  600.   Serial.println(ball_angle);
  601.   ball_y += speed * cos (ball_angle);
  602.   ball_x += speed * sin (ball_angle);
  603.  
  604.   // draw ball
  605.   TFTscreen.fill(255, 255, 255);                          // white
  606.   TFTscreen.circle(ball_x + 0.5, ball_y + 0.5, 2);      // for rounding instead of truncating
  607.  
  608.   float noise = float(random(50)) / 100;
  609.  
  610.   // bounce on sides
  611.   if (ball_y >= (tftHeight - 3) || ball_y <= 2) ball_angle = pi - ball_angle;
  612.   checkBallAngle();
  613.  
  614.   if (ball_x <= 9) {                                                  // bounce by player1
  615.     if  (ball_y < (player1 + 25) && ball_y > player1) {
  616.       digitalWrite(2, HIGH);
  617.       ball_angle = - ball_angle + noise;
  618.       checkBallAngle();
  619.       tone(3, 100, 100);
  620.       counter += 1;
  621.       ball_x++;
  622.       digitalWrite(2, LOW);
  623.     }
  624.     else
  625.     { // player2 wins
  626.       for (int i = 1000; i > 500; i += -1) {
  627.         tone(3, i);
  628.         delay(1);
  629.       }
  630.       noTone(3);
  631.       score2++;
  632.       drawPongScores(score1, score2);
  633.       if (score1 + score2 >= 5) drawPongEnd();
  634.       // erase ball
  635.       TFTscreen.fill(0, 0, 0);                          // black
  636.       TFTscreen.circle(ball_x + 0.5, ball_y + 0.5, 3);      // for rounding instead of truncating
  637.       delay(1000);
  638.       ball_x = 20;
  639.       ball_y = 64;
  640.       ball_angle = 1.5;
  641.       counter = 5;
  642.     }
  643.   }
  644.  
  645.   if (ball_x >= (tftWidth - 11)) {                                        // bounce by player2
  646.     if  (ball_y < (player2 + 25) && ball_y > player2) {
  647.       digitalWrite(2, HIGH);
  648.       ball_angle = - ball_angle + noise;
  649.       checkBallAngle();
  650.       tone(3, 100, 100);
  651.       counter += 1;
  652.       ball_x--;
  653.       digitalWrite(2, LOW);
  654.     }
  655.     else
  656.     { // player1 wins
  657.       for (int i = 1000; i > 500; i += -1) {
  658.         tone(3, i);
  659.         delay(1);
  660.       }
  661.       noTone(3);
  662.       score1++;
  663.       drawPongScores(score1, score2);
  664.       if (score1 + score2 >= 5) drawPongEnd();
  665.       // erase ball
  666.       TFTscreen.fill(0, 0, 0);                          // black
  667.       TFTscreen.circle(ball_x + 0.5, ball_y + 0.5, 3);      // for rounding instead of truncating
  668.       delay(1000);
  669.       ball_x = 140;
  670.       ball_y = 64;
  671.       ball_angle = -1.5;
  672.       counter = 5;
  673.     }
  674.   }
  675.  
  676.   /*   no flickering, erasing/drawing only changes
  677.     if (player1 != player1_old) {
  678.       TFTscreen.fill(0, 0, 0);                          // black
  679.       if (player1_old > player1) {
  680.         TFTscreen.rect(1, player1 + 25, 5, player1_old - player1);             // player1 erase
  681.         TFTscreen.fill(0, 255, 0);                          // green
  682.         TFTscreen.rect(1, player1, 5, player1_old - player1);                  // player1 = green
  683.       }
  684.       if (player1_old < player1) {
  685.         TFTscreen.rect(1, player1_old, 5, player1 - player1_old);             // player1 erase
  686.         TFTscreen.fill(0, 255, 0);                          // green
  687.         TFTscreen.rect(1, player1_old + 25, 5, player1 - player1_old);                  // player1 = green
  688.       }
  689.     }
  690.   */
  691.   if (player1 != player1_old) {
  692.     TFTscreen.fill(0, 0, 0);                          // black
  693.     TFTscreen.rect(1, player1_old, 5, 25);             // player1 erase
  694.     TFTscreen.fill(0, 255, 0);                          // green
  695.     TFTscreen.rect(1, player1, 5, 25);                  // player1 = green
  696.   }
  697.  
  698.   if (player2 != player2_old) {
  699.     TFTscreen.fill(0, 0, 0);                          // black
  700.     TFTscreen.rect(153, player2_old, 5, 25);             // player2 erase
  701.     TFTscreen.fill(0, 0, 255);                          // red
  702.     TFTscreen.rect(153, player2, 5, 25);               // player2 = red
  703.   }
  704.  
  705.   unsigned long target_duration = max(20 - counter / 2, 3);             // target duration of cycle decreases with counter
  706.   unsigned long elapsed = millis() - timestamp;
  707.   if (target_duration > elapsed) {
  708.     delay(target_duration - elapsed);
  709.   }
  710.  
  711. }
  712.  
  713. ////////////////////////////////////////////////////////////////////////////////////////
  714. ////////////////////////////////////////////////////////////////////////////////////////
  715.  
  716. void drawPongEnd() {
  717.  
  718.   TFTscreen.setTextSize(2);
  719.   if (score1 > score2) {
  720.     TFTscreen.stroke(0, 255, 0);                // green
  721.     TFTscreen.text("Player 1", 30, 30);
  722.     TFTscreen.text("wins!", 50, 50);
  723.   }
  724.   else {
  725.     TFTscreen.stroke(0, 0, 255);                // red
  726.     TFTscreen.text("Player 2", 30, 30);
  727.     TFTscreen.text("wins!", 50, 50);
  728.   }
  729.   TFTscreen.setTextSize(1);
  730.   TFTscreen.stroke(255, 0, 0);                // blue
  731.   TFTscreen.text("press button to start", 20, 80);
  732.  
  733.   for (int i = 0; i < 30; i++) {
  734.     tone(3, 2000);
  735.     digitalWrite(2, HIGH);
  736.     delay(20);
  737.     noTone(3);
  738.     digitalWrite(2, LOW);
  739.     delay(20);
  740.   }
  741.   while (1) {                                                               // while wait for reset, short blink
  742.     drawPongScores(score1, score2);
  743.     digitalWrite(2, HIGH);
  744.     delay(20);
  745.     digitalWrite(2, LOW);
  746.   }
  747.   /*
  748.     score1 = 0;
  749.     score2 = 0;
  750.     counter = 0;
  751.     player1 = -1;
  752.     player2 = -1;
  753.     ball_x = 64;
  754.     ball_y = 32;
  755.     drawIntroPong();
  756.     delay(2000);
  757.     TFTscreen.background(0, 0, 0);
  758.     drawPongScores(score1, score2);
  759.     */
  760. }
  761.  
  762. ////////////////////////////////////////////////////////////////////////////////////////
  763. ////////////////////////////////////////////////////////////////////////////////////////
  764.  
  765. void checkBallAngle() {
  766.   while (ball_angle > 2 * pi) {
  767.     ball_angle -= 2 * pi;
  768.   }
  769.   while (ball_angle < 0) {
  770.     ball_angle += 2 * pi;
  771.   }
  772.  
  773.   if (ball_angle > 0 && ball_angle < pi / 4) ball_angle = pi / 4;
  774.   if (ball_angle > 0.75 * pi && ball_angle < pi) ball_angle = 0.75 * pi;
  775.   if (ball_angle > pi && ball_angle < 1.25 * pi) ball_angle = 1.25 * pi;
  776.   if (ball_angle > 1.75 * pi && ball_angle < 2 * pi) ball_angle = 1.75 * pi;
  777. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement