The idea:
This project is part of our education (in the second year) to become an Electronics Technician for Information and Systemengineering.
My team and I got the instruction from our trainer to pimp a table-football game. We had to include several functions like automated goal-counting, manual score modification, sound effects and a display to show the score of both teams.
The beginning:
We started by making plans about how to include all these functions. Our team decided to choose "Capacitive Touch Sensors", mounted inside the 3D-printed housing to increase / decrease the score. We also made the decision to mount the two OLEDs and the speaker inside / on top. The IR Obstacle Sensors will be inside the goals to detect the ball.
After playing with the kicker, we figured out, that the ball will fall out of the goal. So we made a large modification and designed a ramp, which redirects the ball to the side of the kicker.
The prototype:
After finishing all the necessary documentation I started to develop a circuit on a breadboard. And after quite some time I´ve finished the first prototype with working code and an app for your smartphone to control the score, volume etc.
If you want to rebuild this project, you don´t have to use the OR-Gate and the inverter. This is just the methode with interrupts. When you choose to go for the polling version, you have to bridge the resistors and the base emitter rails of the inverters.
Circuit on a PCB:
Unfortunately, we had some problems with the ground polygon, because the mill removed the copper at one side of the PCB-border.
So we had to improve by soldering some wires onto the board.
After soldering all the components, we started testing and had to face the problem of random crashes whenever the IR obstacle sensors have triggered.
Because we couldn´t figure out what the problem was, we choose to modify our board by removing the OR-gate and bridging the inverter of the low-active IR obstacle sensors and parsing the outputs straight to the microcontroller.
After this modification the circuit was working as intended.
By now I think I have figured out what caused this problem. It was the 3.5 mm jack (with no speaker plugged in) which shorted one speaker output to ground. A voltage drop followed and the Arduino restarted. This short circuit destroyed our DFPlayer.
Mounting:
When everything was working as planned, we started to hot glue all the sensors in place.
Testing and tuning:
Because the goal detection wasn´t working accurate enough, we used some hot glue on the ramp to slow down the ball and direct it closer to the IR sensor.
Explaining the code (without interrupt):
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Implementing all the libraries.
Adafruit_SSD1306 OLED_TEAM1(-1);
Adafruit_SSD1306 OLED_TEAM2(-1);
SoftwareSerial mySoftwareSerial(A2, A1);
DFRobotDFPlayerMini myDFPlayer;
uint8_t volume = 10;
uint8_t uintScore[2] = {0, 0};
uint8_t uintState[6] = {255, 255, 255, 255, 255, 255};
uint8_t uintMaxPunkte = 10;
char blData = ' ';
bool playMusic = true;
Classes and variables.
void setup();
void loop();
void serialEvent();
void changeScore();
void win(uint8_t team);
void scr_reset();
void scoreTeam(uint8_t team, char plusMinus);
void show(uint8_t uintOLED, String text);
void led(uint8_t uintLED);
void setupOLED();
Prototypes.
// Debug-mode (remove "//" to start the debug-mode)
//#define DEBUG_SETUP
//#define DEBUG_BL
//#define DEBUG_SCORE
//#define DEBUG_WIN
//#define DEBUG_SCORE_TEAM
//#define DEBUG_SHOW
//#define DEBGU_OLED
Just remove "//" to start the debug-mode e.g OLED ->
#define DEBUG_OLED
// Display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
uint8_t X_POS = 0;
#define Y_POS 10
#define SIZE 2
// Teams
#define TEAM1 0
#define TEAM2 1
// Sound files
#define MUSIC 1
#define POMMES 2
#define SIEG 3
#define TOR 4
#define PFIFF 5
// Lautstärke
#define V_MIN 0
#define V_MAX 30
// Taster, Sensoren
#define UP1 3
#define UP2 4
#define DOWN1 5
#define DOWN2 6
#define IR1 7
#define IR2 8
// Torbeleuchtung
#define TOR1 11
#define TOR2 12
void setup()
{
Serial.begin(9600);
#ifdef DEBUG_SETUP
Serial.println("Starting setup...");
#endif
// LED I/O
pinMode(TOR1, OUTPUT);
pinMode(TOR2, OUTPUT);
setupOLED();
// I/O
pinMode(UP1, INPUT);
pinMode(UP2, INPUT);
pinMode(DOWN1, INPUT);
pinMode(DOWN2, INPUT);
pinMode(IR1, INPUT);
pinMode(IR2, INPUT);
// UART
mySoftwareSerial.begin(9600);
myDFPlayer.begin(mySoftwareSerial);
// DFPlayer
myDFPlayer.volume(volume);
#ifdef DEBUG_SETUP
Serial.print(F("Volume: "));
Serial.println(volume);
#endif
myDFPlayer.play(PFIFF);
delay(1500);
if (playMusic)
myDFPlayer.loop(MUSIC);
show(0, "0");
show(1, "0");
led(TOR1);
led(TOR2);
#ifdef DEBUG_SETUP
Serial.println(F("Setup finished"));
#endif
}
Setting up serial communication, defining inputs and outputs. OLEDs and the DFPlayer.
void loop()
{
// Polling
changeScore();
delay(125);
}
Read the state at the ports every 125 ms.
void serialEvent()
{
// Empfangen von Seriellen Daten per Bluetooth (ausgelöst per Interrupt)
blData = Serial.read();
#ifdef DEBUG_BL
Serial.print(F("Received: <"));
Serial.print(blData);
Serial.println(F(">"));
#endif
switch (blData)
{
// r+
case 'a':
scoreTeam(TEAM2, '+');
break;
// r-
case 'b':
scoreTeam(TEAM2, '-');
break;
// l+
case 'c':
scoreTeam(TEAM1, '+');
break;
// l-
case 'd':
scoreTeam(TEAM1, '-');
break;
// reset
case 'n':
scr_reset();
break;
// Lautstärke erhöhen
case '+':
if (volume < V_MAX)
{
volume++;
myDFPlayer.volume(volume);
#ifdef DEBUG_BL
Serial.print(F("Volume: "));
Serial.println(volume);
#endif
}
break;
// Lautstärke verringern
case '-':
if (volume > V_MIN)
{
volume--;
myDFPlayer.volume(volume);
#ifdef DEBUG_BL
Serial.print(F("Volume: "));
Serial.println(volume);
#endif
}
break;
// Turn on / off music
case 'm':
if (playMusic)
playMusic = false;
else
playMusic = true;
myDFPlayer.stop();
break;
default:
#ifdef DEBUG_BL
Serial.println(F("nada"));
#endif
break;
}
}
Will be triggered when serial data is available.
Commands:
- 'a' -> increase score of team 1
- 'b' -> decrease score of team 1
- 'c' -> increase score of team 2
- 'd' -> decrease score of team 2
- 'n' -> reset score
- '+' -> increase volume
- '-' -> decrease volume
- 'm' -> mute the music
void changeScore()
{
// Status der Pins 6 - 11 einlesen
uint8_t pin = UP1;
for (uint8_t i = 0; i < 6; i++)
{
uintState[i] = digitalRead(pin);
#ifdef DEBUG_SCORE
Serial.print(String(uintState[i]) + " ");
#endif
pin++;
}
#ifdef DEBUG_SCORE
Serial.println("");
#endif
// UP1
if (uintState[0] == 1)
{
#ifdef DEBUG_SCORE
Serial.println(F("UP1"));
#endif
scoreTeam(TEAM1, '+');
}
// IR1
else if (uintState[4] == 0) // inverted
//else if (uintState[4] == 1)
{
#ifdef DEBUG_SCORE
Serial.println(F("IR1"));
#endif
scoreTeam(TEAM1, '+');
myDFPlayer.play(TOR);
led(TOR1);
delay(500);
myDFPlayer.play(PFIFF);
delay(1500);
if (playMusic)
myDFPlayer.loop(MUSIC);
}
// DOWN1
else if (uintState[2] == 1)
{
#ifdef DEBUG_SCORE
Serial.println(F("DOWN1"));
#endif
scoreTeam(TEAM1, '-');
}
// UP2
else if (uintState[1] == 1 )
{
#ifdef DEBUG_SCORE
Serial.println(F("UP2"));
#endif
scoreTeam(TEAM2, '+');
}
// IR2
else if (uintState[5] == 0) // inverted
//else if (uintState[5] == 1)
{
#ifdef DEBUG_SCORE
Serial.println(F("IR2"));
#endif
scoreTeam(TEAM2, '+');
myDFPlayer.play(TOR);
led(TOR2);
delay(500);
myDFPlayer.play(PFIFF);
delay(1500);
if (playMusic)
myDFPlayer.loop(MUSIC);
}
// DOWN2
else if (uintState[3] == 1)
{
#ifdef DEBUG_SCORE
Serial.println(F("DOWN2"));
#endif
scoreTeam(TEAM2, '-');
}
}
Read all inputs and evaluation of results.
void win(uint8_t team)
{
#ifdef DEBUG_WIN
Serial.println("WIN: " + String(team));
#endif
// 10:0 oder 0:10
if (((uintScore[0] == 10) && (uintScore[1] == 0)) || ((uintScore[1] == 10) && (uintScore[0] == 0)))
{
myDFPlayer.volume(25);
myDFPlayer.play(POMMES);
myDFPlayer.volume(volume);
}
else
myDFPlayer.play(SIEG);
if (team == TEAM1)
{
show(TEAM1, "WIN!");
show(TEAM2, "DEFEAT!");
}
else if (team == TEAM2)
{
show(TEAM1, "DEFEAT!");
show(TEAM2, "WIN!");
}
scr_reset();
}
Will display "WIN!" or "DEFEAT!" on the OLEDs and plays a sound.
void scr_reset()
{
uintScore[TEAM1] = 0;
uintScore[TEAM2] = 0;
delay(3000);
show(TEAM1, "0");
show(TEAM2, "0");
myDFPlayer.play(PFIFF);
delay(1500);
if (playMusic)
myDFPlayer.loop(MUSIC);
}
Reset score and restart game.
void scoreTeam(uint8_t team, char plusMinus)
{
#ifdef DEBUG_SCORE_TEAM
Serial.println(F("scoreTeam"));
Serial.print(F("uintScore[TEAM1]: "));
Serial.println(uintScore[TEAM1]);
Serial.print(F("uintScore[TEAM2]: "));
Serial.println(uintScore[TEAM2]);
Serial.print(F("Team: "));
Serial.println(team);
Serial.print(F("plusMinus: "));
Serial.println(plusMinus);
#endif
if (plusMinus == '+')
{
uintScore[team]++;
show(team, String(uintScore[team]));
if ((uintScore[0] >= 10) || (uintScore[1] >= 10))
win(team);
}
else if (plusMinus == '-')
{
if (uintScore[team] > 0)
{
uintScore[team]--;
show(team, String(uintScore[team]));
}
}
}
Function to change the teamscore.
void show(uint8_t uintOLED = 255, String text = "-")
{
// Punktestand an App senden
Serial.print(uintScore[0]);
Serial.print(";");
Serial.println((uintScore[1]));
#ifdef DEBUG_SHOW
Serial.print(F("uintOLED: "));
Serial.println(uintOLED);
Serial.print(F("text: "));
Serial.println(text);
#endif
// Zentrierung des Textes
// Firmenname
if (text.equals("WNJM GmbH"))
X_POS = 12;
else if (text.equals("WIN!"))
X_POS = 37;
else if (text.equals("DEFEAT!"))
X_POS = 22;
// 10 Punkte (zweistellig)
else if (text.equals("10"))
X_POS = 50;
// 0 bis 9 Punkte
else
X_POS = 55;
if (uintOLED == TEAM1)
{
OLED_TEAM1.clearDisplay();
OLED_TEAM1.setCursor(X_POS, Y_POS);
OLED_TEAM1.print(text);
OLED_TEAM1.display();
}
else if (uintOLED == TEAM2)
{
OLED_TEAM2.clearDisplay();
OLED_TEAM2.setCursor(X_POS, Y_POS);
OLED_TEAM2.print(text);
OLED_TEAM2.display();
}
}
Centering and displaying the text on the screen.
void led(uint8_t uintLED)
{
for (int i = 0; i < 10; i++)
{
digitalWrite(uintLED, HIGH);
delay(50);
digitalWrite(uintLED, LOW);
delay(50);
}
}
LEDs in the goal will light up, when function gets called.
void setupOLED()
{
// I2C
pinMode(SCL, OUTPUT);
pinMode(SDA, OUTPUT);
if (!OLED_TEAM1.begin(SSD1306_SWITCHCAPVCC, 0x3C))
{
#ifdef DEBGU_OLED
Serial.println(F("OLED_TEAM1 allocation failed - 0x3C"));
#endif
while(1)
{
digitalWrite(TOR1, HIGH);
delay(1000);
digitalWrite(TOR1, LOW);
delay(1000);
}
}
OLED_TEAM1.clearDisplay();
OLED_TEAM1.setCursor(X_POS, Y_POS);
OLED_TEAM1.setTextSize(SIZE);
OLED_TEAM1.setTextColor(WHITE);
show(TEAM1, "WNJM GmbH");
if (!OLED_TEAM2.begin(SSD1306_SWITCHCAPVCC, 0x3D))
{
#ifdef DEBGU_OLED
Serial.println(F("OLED_TEAM2 allocation failed - 0x3D"));
#endif
while(1)
{
digitalWrite(TOR2, HIGH);
delay(1000);
digitalWrite(TOR2, LOW);
delay(1000);
}
}
OLED_TEAM2.clearDisplay();
OLED_TEAM2.setCursor(X_POS, Y_POS);
OLED_TEAM2.setTextSize(SIZE);
OLED_TEAM2.setTextColor(WHITE);
show(TEAM2, "WNJM GmbH");
#ifdef DEBGU_OLED
Serial.println(F("OLED-setup finished"));
#endif
}
Setting up the OLEDs with address 0x3C and 0x3D (change the address on the back of the OLED by moving a resistor).
Links to the libraries:
Comments
Please log in or sign up to comment.