2 Commits

Author SHA1 Message Date
andrea
c774e11923 Improve arduino_pong.ino using enum to handle game statuses and switch case for better readibility and little memory improvements
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-17 18:42:48 +01:00
andrea
b3f6aeb3fe Show scores on the matrix, show winner message once someone reches 9, add start ball movement delay on start and restart after score, and fix uncentered ball on restart
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-16 22:37:53 +01:00
11 changed files with 360 additions and 214 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
.secrets
.arduino_cache/ .arduino_cache/
bin/ bin/
build/ build/
src/secrets.h

View File

@@ -4,7 +4,6 @@
#include "src/pong_render.h" #include "src/pong_render.h"
#include "src/pong_player.h" #include "src/pong_player.h"
#include "src/pong_ball.h" #include "src/pong_ball.h"
#include "src/arduino_network.h"
// create LED matrix object // create LED matrix object
ArduinoLEDMatrix matrix; ArduinoLEDMatrix matrix;
@@ -22,48 +21,102 @@ byte frame[MATRIX_HEIGHT][MATRIX_WIDTH] = {
}; };
// players coordinates // players coordinates
int p1_start= 1; int players_coords[2]= {1, 4};
int p2_start= 4; int players_scores[2]= {0, 0};
// initials balls coordinates // initials balls coordinates
int ball_x= BALL_RESET_X; int ball_x= BALL_RESET_X;
int ball_y= BALL_RESET_Y; int ball_y= BALL_RESET_Y;
int need_refresh= 1; int need_refresh= true;
int ball_delay= INITIAL_BALL_DELAY; int ball_delay= INITIAL_BALL_DELAY;
long exec_t2= millis(); long exec_t2= millis();
enum game_statuses : uint8_t {
TIMER,
RUN,
SCORE,
GAMEOVER,
WAIT,
};
game_statuses game_status= TIMER;
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
// start LED matrix // start LED matrix
matrix.begin(); matrix.begin();
pinMode(P1_BTN_UP, INPUT_PULLUP); pinMode(P1_BTN_UP, INPUT_PULLUP);
pinMode(P1_BTN_BOTTOM, INPUT_PULLUP); pinMode(P1_BTN_BOTTOM, INPUT_PULLUP);
pinMode(P2_BTN_UP, INPUT_PULLUP); pinMode(P2_BTN_UP, INPUT_PULLUP);
pinMode(P2_BTN_BOTTOM, INPUT_PULLUP); pinMode(P2_BTN_BOTTOM, INPUT_PULLUP);
randomSeed(millis()); randomSeed(millis());
setup_network();
} }
void loop() { void loop() {
long exec_t1= millis(); long exec_t1= millis();
pong_move_p1(p1_start, need_refresh);
pong_move_p2(p2_start, need_refresh); switch (game_status) {
if (exec_t1 - exec_t2 > ball_delay) { case TIMER:
move_ball(ball_x, ball_y, ball_delay, p1_start, p2_start, need_refresh); for (int i = START_TIMER; i >= 0; i--) {
exec_t2= exec_t1; render_timer(frame, i);
delay(1000);
matrix.renderBitmap(frame, MATRIX_HEIGHT, MATRIX_WIDTH);
} }
game_status= RUN;
// delay the first ball movement
exec_t2= millis() + FIRST_START_BALL_DELAY;
break;
case RUN:
pong_move_p1(players_coords[0], need_refresh);
pong_move_p2(players_coords[1], need_refresh);
if (exec_t1 - exec_t2 > ball_delay) {
need_refresh= true;
bool scored= move_ball(ball_x, ball_y, ball_delay, players_coords, players_scores);
if (scored) {
game_status= SCORE;
// delay the ball movement after score
exec_t2= millis() + FIRST_START_BALL_DELAY;
} else exec_t2= exec_t1;
}
// rerender matrix only if something is changed
if (need_refresh) { if (need_refresh) {
render_matrix(frame, p1_start, p2_start, ball_x, ball_y); render_matrix(frame, players_coords, ball_x, ball_y);
matrix.renderBitmap(frame, MATRIX_HEIGHT, MATRIX_WIDTH); matrix.renderBitmap(frame, MATRIX_HEIGHT, MATRIX_WIDTH);
need_refresh= 0; need_refresh= 0;
} }
delay(50); delay(50);
break;
web_server(); case SCORE:
render_score(frame, players_scores);
matrix.renderBitmap(frame, MATRIX_HEIGHT, MATRIX_WIDTH);
delay(1000);
if (players_scores[0] >= MAX_POINTS || players_scores[1] >= MAX_POINTS) {
game_status= GAMEOVER;
}
else game_status= RUN;
break;
case GAMEOVER:
render_winner(frame, matrix, players_scores);
game_status= WAIT;
break;
case WAIT:
// keep showing the winner waiting for a restart
// restart game once one button is pressed
if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW || digitalRead(P2_BTN_UP) == LOW || digitalRead(P2_BTN_BOTTOM) == LOW) {
game_status= TIMER;
players_scores[0]= 0;
players_scores[1]= 0;
}
delay(100);
break;
}
} }

View File

@@ -1,141 +0,0 @@
#include "config.h"
#include "Arduino.h"
#include "WiFiS3.h"
char ssid[]= SECRET_SSID;
char pass[]= SECRET_PASS;
int status= WL_IDLE_STATUS;
WiFiServer server(80);
void printMacAddress(byte mac[]) {
for (int i = 0; i < 6; i++) {
if (i > 0) {
Serial.print(":");
}
if (mac[i] < 16) {
Serial.print("0");
}
Serial.print(mac[i], HEX);
}
Serial.println();
}
void printWifiData() {
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print your MAC address:
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
printMacAddress(mac);
}
void printCurrentNet() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print the MAC address of the router you're attached to:
byte bssid[6];
WiFi.BSSID(bssid);
Serial.print("BSSID: ");
printMacAddress(bssid);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.println(rssi);
// print the encryption type:
byte encryption = WiFi.encryptionType();
Serial.print("Encryption Type:");
Serial.println(encryption, HEX);
Serial.println();
}
void setup_network() {
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
Serial.print("Firmware version: ");
Serial.println(fv);
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// attempt to connect to WiFi network:
// Definisci i parametri di rete prima del loop di connessione
IPAddress local_IP(192, 168, 1, 200);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(9,9,9,9);
WiFi.config(local_IP, gateway, subnet, dns);
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
// you're connected now, so print out the data:
Serial.println("You're connected to the network");
printCurrentNet();
printWifiData();
server.begin();
}
void web_server() {
WiFiClient client = server.available();
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out to the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("<p style=\"font-size:7vw;\">Click <a href=\"/H\">here</a> turn the LED on<br></p>");
client.print("<p style=\"font-size:7vw;\">Click <a href=\"/L\">here</a> turn the LED off<br></p>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}

View File

@@ -1,11 +0,0 @@
#ifndef PONG_NETWORK_H
#define PONG_NETWORK_H
#include "Arduino.h"
void setup_network();
void printMacAddress(byte mac[]);
void printWifiData();
void printCurrentNet();
void web_server();
#endif

View File

@@ -1,11 +1,3 @@
#if __has_include("secrets.h")
#include "secrets.h"
#else
#define SECRET_SSID "Fallback_SSID"
#define SECRET_PASS "Fallback_PASS"
#warning "⚠️ secrets.h not found"
#endif
#define P1_BTN_UP 12 #define P1_BTN_UP 12
#define P1_BTN_BOTTOM 11 #define P1_BTN_BOTTOM 11
#define P2_BTN_UP 10 #define P2_BTN_UP 10
@@ -17,3 +9,7 @@
#define BALL_RESET_Y (MATRIX_HEIGHT / 2) #define BALL_RESET_Y (MATRIX_HEIGHT / 2)
#define BAR_LENGTH 3 #define BAR_LENGTH 3
#define INITIAL_BALL_DELAY 200 #define INITIAL_BALL_DELAY 200
#define MAX_POINTS 9
#define START_TIMER 3
#define FIRST_START_BALL_DELAY 500

184
src/font.h Normal file
View File

@@ -0,0 +1,184 @@
#ifndef FONT_H
#define FONT_H
#include <Arduino.h>
const uint32_t pone_wins[][4] = {
{
0x78,
0xc4847844,
0x440e000,
500
},
{
0x11,
0x1101501,
0x501f0000,
500
},
{
0xe,
0x400400,
0x400e0000,
500
},
{
0x9,
0xd00b00,
0x90090000,
500
},
{
0xf,
0x800f00,
0x100f0000,
500
}
};
const uint32_t ptwo_wins[][4] = {
{
0x79,
0xe48279e4,
0x1041e000,
500
},
{
0x11,
0x1101501,
0x501f0000,
500
},
{
0xe,
0x400400,
0x400e0000,
500
},
{
0x9,
0xd00b00,
0x90090000,
500
},
{
0xf,
0x800f00,
0x100f0000,
500
}
};
const byte font_pong[10][8][3] = {
// Number 0
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 1
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 0 },
{ 0, 1, 0 },
{ 0, 1, 0 },
{ 0, 1, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 2
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 3
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 4
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
// Number 5
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 6
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 0, 0 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 7
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
// Number 8
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 9
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
};
#endif

View File

@@ -5,54 +5,60 @@
// used to increase speed when game is too easy // used to increase speed when game is too easy
int hits= 0; int hits= 0;
int p1_score= 0;
int p2_score= 0;
// initially ball has no movements // initially ball has no movements
// once game/round starts, balls gets random x and y movements // once game/round starts, balls gets random x and y movements
int ball_move_x= 0; int ball_move_x= 0;
int ball_move_y= 0; int ball_move_y= 0;
void point_scored(int &ball_x, int &ball_y, int &ball_delay) { void random_ball_movement(int &ball_move_x, int &ball_move_y) {
if (random(2) == 0) ball_move_x= 1;
else ball_move_x= -1;
if (random(2) == 0) ball_move_y= 1;
else ball_move_y= -1;
}
void point_scored(int &ball_x, int &ball_y, int &ball_delay, int players_scores[2], int &ball_move_x, int &ball_move_y) {
ball_x= BALL_RESET_X; ball_x= BALL_RESET_X;
ball_y= BALL_RESET_Y; ball_y= BALL_RESET_Y;
random_ball_movement(ball_move_x, ball_move_y);
Serial.print("P1: "); Serial.print("P1: ");
Serial.print(p1_score); Serial.print(players_scores[0]);
Serial.print(" - "); Serial.print(" - ");
Serial.print("P2: "); Serial.print("P2: ");
Serial.print(p2_score); Serial.print(players_scores[1]);
Serial.println(); Serial.println();
hits= 0; hits= 0;
ball_delay= INITIAL_BALL_DELAY; ball_delay= INITIAL_BALL_DELAY;
} }
void move_ball(int &ball_x, int &ball_y, int &ball_delay, int p1_start, int p2_start, int &need_refresh) { bool move_ball(int &ball_x, int &ball_y, int &ball_delay, int players_coords[2], int players_scores[2]) {
need_refresh= 1;
if (ball_x < 0 || ball_x > MATRIX_WIDTH-1 || ball_y < 0 || ball_y > MATRIX_HEIGHT-1) { if (ball_x < 0 || ball_x > MATRIX_WIDTH-1 || ball_y < 0 || ball_y > MATRIX_HEIGHT-1) {
// ball out of matrix limits // ball out of matrix limits
ball_x= BALL_RESET_X; ball_x= BALL_RESET_X;
ball_y= BALL_RESET_Y; ball_y= BALL_RESET_Y;
return; return false;
} }
bool scored= false;
// if ball is not moving, get random direction // if ball is not moving, get random direction
// this is the initial position // this is the initial position
if (ball_move_x == 0 || ball_move_y == 0) { if (ball_move_x == 0 || ball_move_y == 0) {
// extract random number between 0 or 1 to select the directions // extract random number between 0 or 1 to select the directions
if (random(2) == 0) ball_move_x= 1; random_ball_movement(ball_move_x, ball_move_y);
else ball_move_x= -1;
if (random(2) == 0) ball_move_y= 1;
else ball_move_y= -1;
} }
else if (ball_x == 0) { else if (ball_x == 0) {
// if p1 collision: reverse x, go left // if p1 collision: reverse x, go left
if (!ball_player_collision(p1_start, ball_y)) { if (!ball_player_collision(players_coords[0], ball_y)) {
// else p2 score, reset board // else p2 score, reset board
p2_score += 1; players_scores[1] += 1;
scored= true;
Serial.println("Player 2 Scores"); Serial.println("Player 2 Scores");
point_scored(ball_x, ball_y, ball_delay); point_scored(ball_x, ball_y, ball_delay, players_scores, ball_move_x, ball_move_y);
return true;
} }
else { else {
hits += 1; hits += 1;
@@ -60,11 +66,13 @@ void move_ball(int &ball_x, int &ball_y, int &ball_delay, int p1_start, int p2_s
} }
} }
else if (ball_x == MATRIX_WIDTH-1) { else if (ball_x == MATRIX_WIDTH-1) {
if (!ball_player_collision(p2_start, ball_y)) { if (!ball_player_collision(players_coords[1], ball_y)) {
// else p1 score, reset board // else p1 score, reset board
p1_score += 1; players_scores[0] += 1;
scored= true;
Serial.println("Player 1 Scores"); Serial.println("Player 1 Scores");
point_scored(ball_x, ball_y, ball_delay); point_scored(ball_x, ball_y, ball_delay, players_scores, ball_move_x, ball_move_y);
return true;
} }
else { else {
hits += 1; hits += 1;
@@ -79,10 +87,11 @@ void move_ball(int &ball_x, int &ball_y, int &ball_delay, int p1_start, int p2_s
if (hits >= 6 && ball_delay >= 80) { if (hits >= 6 && ball_delay >= 80) {
// increase ball speed // increase ball speed
hits = 0; hits= 0;
ball_delay -= 20; ball_delay-= 20;
} }
ball_x+= ball_move_x; ball_x+= ball_move_x;
ball_y+= ball_move_y; ball_y+= ball_move_y;
return scored;
} }

View File

@@ -1,7 +1,6 @@
#ifndef PONG_BALL_H #ifndef PONG_BALL_H
#define PONG_BALL_H #define PONG_BALL_H
void point_scored(); bool move_ball(int &ball_x, int &ball_y, int &loop_delay, int players_coords[2], int players_scores[2]);
void move_ball(int &ball_x, int &ball_y, int &loop_delay, int p1_start, int p2_start, int &need_refresh); void foo();
#endif #endif

View File

@@ -13,21 +13,21 @@ int ball_player_collision(int player, int ball_y) {
int pong_move_p1(int &p1_start, int &need_refresh) { int pong_move_p1(int &p1_start, int &need_refresh) {
if (digitalRead(P1_BTN_UP) == LOW && p1_start > 0) { if (digitalRead(P1_BTN_UP) == LOW && p1_start > 0) {
p1_start -= 1; p1_start -= 1;
need_refresh= 1; need_refresh= true;
} }
else if (digitalRead(P1_BTN_BOTTOM) == LOW && p1_start < 5) { else if (digitalRead(P1_BTN_BOTTOM) == LOW && p1_start < 5) {
p1_start += 1; p1_start += 1;
need_refresh= 1; need_refresh= true;
} }
} }
int pong_move_p2(int &p2_start, int &need_refresh) { int pong_move_p2(int &p2_start, int &need_refresh) {
if (digitalRead(P2_BTN_UP) == LOW && p2_start > 0) { if (digitalRead(P2_BTN_UP) == LOW && p2_start > 0) {
p2_start -= 1; p2_start -= 1;
need_refresh= 1; need_refresh= true;
} }
else if (digitalRead(P2_BTN_BOTTOM) == LOW && p2_start < 5) { else if (digitalRead(P2_BTN_BOTTOM) == LOW && p2_start < 5) {
p2_start += 1; p2_start += 1;
need_refresh= 1; need_refresh= true;
} }
} }

View File

@@ -1,22 +1,75 @@
#include <Arduino.h> #include <Arduino.h>
#include "Arduino_LED_Matrix.h"
#include "config.h" #include "config.h"
#include "font.h"
void render_matrix(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int p1_start, int p2_start, int ball_x, int ball_y) { void clear_matrix(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH]) {
// clear
for (int x=0; x < MATRIX_WIDTH; x++) { for (int x=0; x < MATRIX_WIDTH; x++) {
for (int y=0; y < MATRIX_HEIGHT; y++) { for (int y=0; y < MATRIX_HEIGHT; y++) {
frame[y][x]= 0; frame[y][x]= 0;
} }
} }
}
void render_matrix(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int players_coords[2], int ball_x, int ball_y) {
clear_matrix(frame);
int player_one= players_coords[0];
int player_two= players_coords[1];
// players coords // players coords
for (int i= p1_start; i < p1_start+BAR_LENGTH; i++) { for (int i= player_one; i < player_one+BAR_LENGTH; i++) {
frame[i][0]= 1; frame[i][0]= 1;
} }
for (int i= p2_start; i < p2_start+BAR_LENGTH; i++) { for (int i= player_two; i < player_two+BAR_LENGTH; i++) {
frame[i][MATRIX_WIDTH-1]= 1; frame[i][MATRIX_WIDTH-1]= 1;
} }
// ball coords // ball coords
frame[ball_y][ball_x]= 1; frame[ball_y][ball_x]= 1;
} }
void render_score(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int players_scores[2]) {
clear_matrix(frame);
int player_one= players_scores[0];
int player_two= players_scores[1];
if (player_one > 9) player_one = 9;
if (player_two > 9) player_two = 9;
// player score separator (-)
frame[4][5]= 1;
frame[4][6]= 1;
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
frame[h][w+1]= font_pong[player_one][h][w];
}
}
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
frame[h][w+8]= font_pong[player_two][h][w];
}
}
}
void render_timer(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int seconds) {
clear_matrix(frame);
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
frame[h][w+5]= font_pong[seconds][h][w];
}
}
}
void render_winner(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], ArduinoLEDMatrix &matrix, int players_scores[2]) {
clear_matrix(frame);
// check winner
if (players_scores[0] > players_scores[1]) {
Serial.println("Player 1 wins!!!");
matrix.loadSequence(pone_wins);
}
else {
Serial.println("Player 2 wins!!!");
matrix.loadSequence(ptwo_wins);
}
matrix.play(true);
}

View File

@@ -3,7 +3,11 @@
#include <Arduino.h> #include <Arduino.h>
#include "Arduino_LED_Matrix.h" #include "Arduino_LED_Matrix.h"
#include "config.h"
void render_matrix(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int p1_start, int p2_start, int ball_x, int ball_y); void render_matrix(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int players_coords[2], int ball_x, int ball_y);
void render_score(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int players_scores[2]);
void render_timer(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], int seconds);
void render_winner(byte frame[MATRIX_HEIGHT][MATRIX_WIDTH], ArduinoLEDMatrix &matrix, int players_scores[2]);
#endif #endif