PONG
Платформа ATWatch благодаря наличию NRF может реализовывать проекты с беспроводной связью. Данный проект представляет из себя игру Пинг-понг, в которую можно играть вдвоём на разных часах по беспроводной связи.
Код приёмника (RX):
#define OLED_DC 26
#define OLED_CS 31
#define OLED_RESET 24
#define BUZZER 15
#define BTN1 2
#define BTN2 12
#define BTN3 13
#include
#include "nRF24L01.h"
#include "GyverButton.h"
#include "RF24.h"
#include
#include
RF24 radio(3, 4);
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
GButton btn1(BTN1, LOW_PULL, NORM_OPEN);
GButton btn2(BTN2, LOW_PULL, NORM_OPEN);
GButton btn3(BTN3, LOW_PULL, NORM_OPEN);
byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; //возможные номера труб
struct point_t
{
byte x, y;
};
struct package_t
{
byte gameStatus;
byte scoreP1;
byte scoreP2;
byte txPlayerPos;
point_t ballPos;
point_t ballVel;
};
int x_pixels = 128;
int y_pixels = 64;
//Paddle Parameters
int paddle_height = 10;
int paddle_width = 3;
byte myPos = 20;
package_t package;
void setup()
{
btn1.setStepTimeout(50);
btn1.setTimeout(50);
btn3.setStepTimeout(50);
btn3.setTimeout(50);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
radio.begin(); //активировать модуль
radio.setAutoAck(1); //режим подтверждения приёма, 1 вкл 0 выкл
radio.setRetries(0, 15); //(время между попыткой достучаться, число попыток)
radio.enableAckPayload(); //разрешить отсылку данных в ответ на входящий сигнал
//radio.enableDynamicPayloads(); // Разрешить динамически изменяемый размер блока данных на всех трубах.
radio.setPayloadSize(10); //размер пакета, в байтах
radio.openReadingPipe(1, address[0]); //хотим слушать трубу 0
radio.setChannel(0x60); //выбираем канал (в котором нет шумов!)
radio.setPALevel (RF24_PA_MAX); //уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
radio.setDataRate (RF24_2MBPS); //скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
radio.powerUp(); //начать работу
radio.startListening(); //начинаем слушать эфир, мы приёмный модуль
}
void loop(void)
{
Buttons();
byte pipeNo;
while (radio.available(&pipeNo)) // слушаем эфир со всех труб
{
radio.read(&package, sizeof(package) ); // чиатем входящий сигнал
radio.writeAckPayload(pipeNo, &myPos, sizeof(myPos) ); // отправляем обратно то что приняли
pongLoop();
}
}
void Buttons()
{
btn1.tick();
btn2.tick();
btn3.tick();
if (btn1.isStep() || btn1.isClick())
{
if (myPos < (y_pixels - paddle_height))
myPos += 2;
}
if (btn3.isStep() || btn3.isClick())
{
if (myPos > 0)
myPos -= 2;
}
}
void pongLoop()
{
if (package.ballPos.x > x_pixels - 1)
{
tone(BUZZER, 50, 100);
delay(50);
tone(BUZZER, 50, 100);
}
else if (package.ballPos.x < 1)
{
tone(BUZZER, 50, 100);
delay(50);
tone(BUZZER, 50, 100);
}
// Draw pong elements to display:
display.clearDisplay();
display.drawPixel(package.ballPos.x, package.ballPos.y, WHITE);
display.fillRect(0, package.txPlayerPos, paddle_width, paddle_height, WHITE);
display.fillRect(x_pixels - paddle_width , myPos, paddle_width, paddle_height, WHITE);
// Display scores
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(display.width() / 4, 0);
display.println(package.scoreP1);
display.setCursor(display.width() * 3 / 4, 0);
display.println(package.scoreP2);
// Display all elements
display.display();
// Check for ball bouncing off paddles:
if (ball_on_right_paddle())
{
tone(BUZZER, 300, 100);
}
else if (ball_on_left_paddle())
{
tone(BUZZER, 250, 100);
}
}
bool ball_on_right_paddle()
{
// If ball is heading towards paddle and is at the surface of paddle between the top and bottom of the paddle, then it's a hit
return (package.ballPos.x == x_pixels - paddle_width - 1 && package.ballPos.y >= myPos && package.ballPos.y <= (myPos + paddle_height) && package.ballVel.x == 1);
}
bool ball_on_left_paddle()
{
return (package.ballPos.x == paddle_width - 1 && package.ballPos.y >= package.txPlayerPos && package.ballPos.y <= (package.txPlayerPos + paddle_height));
}
Код передатчика (TX):
#define OLED_DC 26
#define OLED_CS 31
#define OLED_RESET 24
#define BUZZER 15
#define BTN1 2
#define BTN2 12
#define BTN3 13
#include
#include "nRF24L01.h"
#include "GyverButton.h"
#include "RF24.h"
#include
#include
RF24 radio(3, 4);
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
GButton btn1(BTN1, LOW_PULL, NORM_OPEN);
GButton btn2(BTN2, LOW_PULL, NORM_OPEN);
GButton btn3(BTN3, LOW_PULL, NORM_OPEN);
byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; //возможные номера труб
struct point_t
{
byte x, y;
};
struct package_t
{
byte gameStatus;
byte scoreP1;
byte scoreP2;
byte txPlayerPos;
point_t ballPos;
point_t ballVel;
};
int x_pixels = 128;
int y_pixels = 64;
//Paddle Parameters
int paddle_height = 10;
int paddle_width = 3;
byte rxPlayerPos;
point_t ballPos = { 10, 20};
package_t package;
void setup()
{
btn1.setStepTimeout(50);
btn1.setTimeout(50);
btn3.setStepTimeout(50);
btn3.setTimeout(50);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
radio.begin(); //активировать модуль
radio.setAutoAck(1); //режим подтверждения приёма, 1 вкл 0 выкл
radio.setRetries(0, 2); //(время между попыткой достучаться, число попыток)
radio.enableAckPayload(); //разрешить отсылку данных в ответ на входящий сигнал
//radio.enableDynamicPayloads(); // Разрешить динамически изменяемый размер блока данных на всех трубах.
radio.setPayloadSize(10); //размер пакета, в байтах
radio.openWritingPipe(address[0]); //мы - труба 0, открываем канал для передачи данных
radio.setChannel(0x60); //выбираем канал (в котором нет шумов!)
radio.setPALevel (RF24_PA_MAX); //уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
radio.setDataRate (RF24_2MBPS); //скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
radio.powerUp(); //начать работу
radio.stopListening(); //не слушаем радиоэфир, мы передатчик
Init();
}
void Init()
{
package.ballPos = ballPos;
package.txPlayerPos = package.txPlayerPos;
package.scoreP1 = 0;
package.scoreP2 = 0;
package.gameStatus = 0;
package.ballVel = {1, 1};
}
void loop(void)
{
Buttons();
radio.writeFast(&package, sizeof(package));
while (radio.available())
{
radio.read(&rxPlayerPos, sizeof(rxPlayerPos) );
}
pongLoop();
delay(20);
}
void Buttons()
{
btn1.tick();
btn2.tick();
btn3.tick();
if (btn1.isStep() || btn1.isClick())
{
if (package.txPlayerPos < (y_pixels - paddle_height))
package.txPlayerPos += 2;
}
if (btn3.isStep() || btn3.isClick())
{
if ( package.txPlayerPos > 0)
package.txPlayerPos -= 2;
}
}
void pongLoop()
{
// Check for ball hitting a wall:
if (package.ballPos.x > x_pixels - 1)
{
ball_reset(false);
package.scoreP1 += 1;
tone(BUZZER, 50, 100);
delay(50);
tone(BUZZER, 50, 100);
}
else if (package.ballPos.x < 1)
{
ball_reset(true);
package.scoreP2 += 1;
tone(BUZZER, 50, 100);
delay(50);
tone(BUZZER, 50, 100);
Serial.println(2);
}
// Check for ball bouncing off ceiling:
if (package.ballPos.y > y_pixels - 1 || package.ballPos.y < 0)
{
package.ballVel.y = -package.ballVel.y;
}
// Check for ball bouncing off paddle:
// Update ball position:
package.ballPos.x += package.ballVel.x;
package.ballPos.y += package.ballVel.y;
// Draw pong elements to display:
display.clearDisplay();
display.drawPixel(package.ballPos.x, package.ballPos.y, WHITE);
display.fillRect(0, package.txPlayerPos, paddle_width, paddle_height, WHITE);
display.fillRect(x_pixels - paddle_width , rxPlayerPos, paddle_width, paddle_height, WHITE);
// Display scores
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(display.width() / 4, 0);
display.println(package.scoreP1);
display.setCursor(display.width() * 3 / 4, 0);
display.println(package.scoreP2);
// Display all elements
display.display();
// Check for ball bouncing off paddles:
if (ball_on_right_paddle())
{
package.ballVel.x = -package.ballVel.x;
tone(BUZZER, 300, 100);
}
else if (ball_on_left_paddle())
{
package.ballVel.x = -package.ballVel.x;
tone(BUZZER, 250, 100);
}
}
bool ball_on_right_paddle()
{
// If ball is heading towards paddle and is at the surface of paddle between the top and bottom of the paddle, then it's a hit
return (package.ballPos.x == x_pixels - paddle_width - 1 && package.ballPos.y >= rxPlayerPos && package.ballPos.y <= (rxPlayerPos + paddle_height) && package.ballVel.x == 1);
}
bool ball_on_left_paddle()
{
return (package.ballPos.x == paddle_width + 1 && package.ballPos.y >= package.txPlayerPos && package.ballPos.y <= (package.txPlayerPos + paddle_height));
}
void ball_reset(bool left)
{
//If left is true, then ball should launch from the left, otherwise launch from the right
//Ball should launch at a random Y location and at a random Y velocity
package.ballPos.y = random(display.height());
if (random(2) > 0)
{
package.ballVel.y = 1;
}
else
{
package.ballVel.y = -1;
}
if (left)
{
package.ballVel.x = 1;
package.ballPos.x = paddle_width - 1;
}
else
{
package.ballVel.x = -1;
package.ballPos.x = display.width() - paddle_width;
}
}