Hardware components | ||||||
![]() |
| × | 2 | |||
![]() |
| × | 2 | |||
![]() |
| × | 2 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
- Automatic up and down counting
- Digital display of the result
- Manual correction of the result
- Output of different sound effects
- Light effects
Two buttons, two displays, two speakers, a power supply, a 3-button matrix and an LED bar were attached to the football table.
Taster
A 17 mm hole was drilled for the two pushbuttons at
a distance of 3 cm from the edge.
The two keys are used to stop and resume the time.
Strom
The toggle switch is located below the push button
on gate 1. The hole has the
dimensions of 13.5 mm x 17 mm.
It is used to switch the power supply on and off.
OLED
The two displays are located in the center of the two
gates and both have a 3D-printed frame.
Frame. The frames have 2 holes for mounting
and a small recess for the pins.
The displays show the score and the
names of the teams.
Touch matrix
The touch matrix is located on the slope of the
Gate 1, it requires an approx. 1.
cm recess for the ribbon cable.
With the touch matrix you can switch between the
both players to count up and down manually.
For each of the two speakers
4 small screws are required.
The speakers are located to
the left of the gates
The cable for the ball insertion is about 80 cm
long and all others are about 50 cm long.
The cables are connected to the printed circuit
board with the help of cable lugs and
connected to the circuit board with the help
of cable lugs and screw terminals. The circuit
board is located under the tabletop kicker.
It is attached to the underside of the foosball
table with 2 3D-printed clips.
The LED strip is glued with a 2-component adhesive
glued into the inside of the playing field.
The ball insert was manufactured in a 3D printer,
in it is installed a light barrier, when this is
is interrupted, the game starts and he sound.
The LEDs show the colors of the teams and
a small advertisement.
The 3D manufactured goals also have a
light barrier, when this is interrupted, the goal counter
for the respective team is
automatically increased by one and a shout of joy is heard
On the one hand, the 3 IR sensors are connected to the microcontroller 2, which emit a low signal if they are not interrupted. The signals are passed on to the IC, where they are received and evaluated. As long as the IR sensors have a LOW state, they are converted in the IC into a HIGH.
Microcontroller 2
Microcontroller 1
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include < .h>
#include <SoftwareSerial.h>
#include <DFMiniMp3.h>
//Taster 1-3 manuelle Toranpassung Taster 4-5 manueller Start/Stopp
const uint8_t taster1=4;
const uint8_t taster2=5;
const uint8_t taster3=6;
const uint8_t taster4=12;
const uint8_t taster5=7;
//werte auf der der Werbetimer resetet wird
const uint8_t werbedauermin=0,werbedauersec=5;
const uint8_t pinArduinoRX=A0;
const uint8_t pinArduinoTX=A1;
const uint8_t pinMP3_1RX=8;
const uint8_t pinMP3_1TX=9;
const uint8_t pinMP3_2RX=10;
const uint8_t pinMP3_2TX=11;
//verschiedene Zeiten in ms für die Spielstandansage
const uint32_t ansagezeit100=500;
const uint32_t ansagezeit1112=500;
const uint32_t ansagezeit10=500;
const uint32_t ansagezeit1=500;
uint8_t noansagedelay;
uint8_t zeitansagestatus;
uint32_t zuletztgepollt;
//Werte auf der der kommentatortimer resetet wird bei kommentar oder "Ereignis"
const uint8_t kommentatormin=0;
const uint8_t kommentatorsec=25;
volatile uint8_t flagAktualisiereDislpay;
volatile uint8_t flagWerbung;
volatile uint8_t flagGewinner;
volatile uint8_t flagKommentar;
uint8_t statusManuelleToranpassung;
const uint16_t sounddelay=300;
//die Klasse wird zur benutzung der Bibliothek DFMiniMp3 benötigt wird hier aber nicht verwendet
class Mp3Notify
{
public:
static void OnError(uint16_t errorCode){}
static void OnUsbRemoved(uint16_t errorCode){}
static void OnUsbInserted(uint16_t errorCode){}
static void OnUsbOnline(uint16_t errorCode){}
static void OnPlayFinished(uint16_t globalTrack){}
static void OnCardOnline(uint16_t code){ }
static void OnCardInserted(uint16_t code){}
static void OnCardRemoved(uint16_t code){}
};
//(1)
const uint8_t pinBTRX=A0,pinBTTX=A1;
SoftwareSerial BTSerial(pinBTRX, pinBTTX);
SoftwareSerial soundSerial1(pinMP3_1RX, pinMP3_1TX);
SoftwareSerial soundSerial2(pinMP3_2RX, pinMP3_2TX);
DFMiniMp3<SoftwareSerial, Mp3Notify> mp3_1(soundSerial1);
DFMiniMp3<SoftwareSerial, Mp3Notify> mp3_2(soundSerial2);
#define OLED_RESET 4 // nicht genutzt bei diesem Display
Adafruit_SSD1306 display(OLED_RESET);
//selbstdefinitzon von passenden Datentypen
struct farbe
{
uint8_t r;
uint8_t g;
uint8_t b;
};
struct timer{
volatile uint8_t minuten;
volatile uint8_t sekunden;
};
struct team
{
farbe teamfarbe;
String teamname;
String kurzel;
uint8_t tore;
};
struct werbeanzeige
{
timer zeitBisNachsteWerbung;
uint8_t zustand;
farbe curFarbe;
};
struct einstellungen{
werbeanzeige werbung;
timer spielzeit, kommentatorspruch;
team team1,team2;
uint8_t spielmodus; //0->Torspiel, 1->Zeitspiel
uint8_t spielstatus;//0-> Spiel inaktiv, 1->Spiel am gange, 2->zeitablaufend(Zeitspiel im gange)
uint8_t maxTore;
};
einstellungen curSettings;
//von der Teamleitung vorgegebene Farben
farbe _0,_1,_2,_3,_4,_5,_6,_7,_8,_9;
//(1)
void testNeopixel()
{
//sende den Befehl LEDstripe zu testen
Serial.write(0x00);
}
//Nacheinanderabspielen der Zahlsounddateien
void zahlansagen(uint8_t zahl,DFMiniMp3<SoftwareSerial, Mp3Notify> mp3)
{
if(zahl>99)
{
if(zahl>199)
{
mp3.playMp3FolderTrack(32);//"200"
//zeitansagestatus=1;
}
else
{
mp3.playMp3FolderTrack(31);//"100"
}
waitMilliseconds(ansagezeit100);
}
switch(zahl%100)
{
case 0:
if(zahl<100)
{
mp3.playMp3FolderTrack(10);//"0"
waitMilliseconds(ansagezeit1);
}
break;
case 11:
mp3.playMp3FolderTrack(33);//"11"
waitMilliseconds(ansagezeit1112);
break;
case 12:
mp3.playMp3FolderTrack(34);//"12"
waitMilliseconds(ansagezeit1112);
break;
default:
if(zahl%10!=0)
{
mp3.playMp3FolderTrack((zahl%10)+10);//letzte Ziffer ansagen
waitMilliseconds(ansagezeit1);
}
if(zahl%100>9)
mp3.playMp3FolderTrack(35);//"und"
switch(zahl%100)
{
case 10 ... 19:
mp3.playMp3FolderTrack(21);
break;
case 20 ... 29:
mp3.playMp3FolderTrack(22);
break;
case 30 ... 39:
mp3.playMp3FolderTrack(23);
break;
case 40 ... 49:
mp3.playMp3FolderTrack(24);
break;
case 50 ... 59:
mp3.playMp3FolderTrack(25);
break;
case 60 ... 69:
mp3.playMp3FolderTrack(26);
break;
case 70 ... 79:
mp3.playMp3FolderTrack(27);
break;
case 80 ... 89:
mp3.playMp3FolderTrack(28);
break;
case 90 ... 99:
mp3.playMp3FolderTrack(29);
break;
default:
break;
}
}
}
//Ansagen des Spielstandes
void spielstandAnsagen(DFMiniMp3<SoftwareSerial, Mp3Notify> mp3)
{
zahlansagen(curSettings.team1.tore,mp3);
//mp3_1.playMp3FolderTrack(36);//"zu"
waitMilliseconds(150);
mp3.playMp3FolderTrack(36);//"zu"
waitMilliseconds(400);
zahlansagen(curSettings.team2.tore,mp3);
}
//zu den Zeitpunkten 5:00, 2:00, 1:00, 0:30 und von 9 abwärtszählend Zeit ansagen
void zeitansage()
{
switch(curSettings.spielzeit.minuten)
{
case 5:
if(curSettings.spielzeit.sekunden==0&&zeitansagestatus!=1)
{
zeitansagestatus=1;
mp3_1.playMp3FolderTrack(15); //"5"
mp3_2.playMp3FolderTrack(15); //"5"
waitMilliseconds(ansagezeit1);
mp3_1.playMp3FolderTrack(37); //"Minuten"
mp3_2.playMp3FolderTrack(37); //"Minuten"
}
break;
case 2:
if(curSettings.spielzeit.sekunden==0&&zeitansagestatus!=2)
{
zeitansagestatus=2;
mp3_1.playMp3FolderTrack(12); //"2"
mp3_2.playMp3FolderTrack(12); //"2"
waitMilliseconds(ansagezeit1);
mp3_1.playMp3FolderTrack(37); //"Minuten"
mp3_2.playMp3FolderTrack(37); //"Minuten"
}
break;
case 1:
if(curSettings.spielzeit.sekunden==0&&zeitansagestatus!=3)
{
zeitansagestatus=3;
mp3_1.playMp3FolderTrack(11); //"1"
mp3_2.playMp3FolderTrack(11); //"1"
waitMilliseconds(ansagezeit1);
mp3_1.playMp3FolderTrack(37); //"Minuten"
mp3_2.playMp3FolderTrack(37); //"Minuten"
}
break;
case 0:
switch(curSettings.spielzeit.sekunden)
{
case 30:
if(zeitansagestatus!=4)
{
zeitansagestatus=4;
mp3_2.playMp3FolderTrack(23);
waitMilliseconds(ansagezeit10);
mp3_2.playMp3FolderTrack(38);
}
break;
case 10:
if(zeitansagestatus!=5)
{
zeitansagestatus=5;
mp3_2.playMp3FolderTrack(21);
}
break;
case 9:
if(zeitansagestatus!=6)
{
zeitansagestatus=6;
mp3_2.playMp3FolderTrack(19);
}
break;
case 8:
if(zeitansagestatus!=7)
{
zeitansagestatus=7;
mp3_2.playMp3FolderTrack(18);
}
break;
case 7:
if(zeitansagestatus!=8)
{
zeitansagestatus=8;
mp3_2.playMp3FolderTrack(17);
}
break;
case 6:
if(zeitansagestatus!=9)
{
zeitansagestatus=9;
mp3_2.playMp3FolderTrack(16);
}
break;
case 5:
if(zeitansagestatus!=10)
{
zeitansagestatus=10;
mp3_2.playMp3FolderTrack(15);
}
break;
case 4:
if(zeitansagestatus!=11)
{
zeitansagestatus=11;
mp3_2.playMp3FolderTrack(14);
}
break;
case 3:
if(zeitansagestatus!=12)
{
zeitansagestatus=12;
mp3_2.playMp3FolderTrack(13);
}
break;
case 2:
if(zeitansagestatus!=13)
{
zeitansagestatus=13;
mp3_2.playMp3FolderTrack(12);
}
break;
case 1:
if(zeitansagestatus!=14)
{
zeitansagestatus=14;
mp3_2.playMp3FolderTrack(11);
}
break;
}
}
}
//während des wartens für z.B. Soundausgabe sollen Taster gepollt werden damit die Tatendrücke erkannt werden daher kein delay()
void waitMilliseconds(uint16_t msWait)
{
uint32_t start = millis();
while ((millis() - start) < msWait)
{
polleTaster();
}
}
//(1)
void testSound()
{
mp3_1.setVolume(20);
mp3_1.playMp3FolderTrack(1); // sd:/mp3/0001.mp3 (Anpfiff)
waitMilliseconds(5000);
mp3_1.pause();
mp3_2.setVolume(20);
mp3_2.playMp3FolderTrack(1); // sd:/mp3/0001.mp3 (Anpfiff)
waitMilliseconds(5000);
mp3_2.pause();
}
//je nach Spielmodus relevante Informationen auf den Oled Displays anzeigen
void aktualisiereDisplay()
{
flagAktualisiereDislpay=0;
display.clearDisplay();
switch(curSettings.spielmodus)
{
case 0://Tormodus
display.setTextSize(1);
display.setCursor(0,0);
display.print(curSettings.team1.teamname);
for(int i=10-curSettings.team1.teamname.length()+10-curSettings.team2.teamname.length();i>0;i--)
display.print(' ');
display.print(curSettings.team2.teamname);
display.setCursor(0,10);
display.setTextSize(3);
//Formatierungsausgaben
if(curSettings.team1.tore<100)
display.print(' ');
if(curSettings.team1.tore<10)
display.print(' ');
display.print(curSettings.team1.tore);
display.print(":");
display.print(curSettings.team2.tore);
display.display();
break;
case 1://Zeitmodus
display.setTextSize(1);
display.setCursor(0,0);
for(int i=3-curSettings.team1.kurzel.length();i>0;i--)
display.print(' ');
display.print(curSettings.team1.kurzel);
display.setTextSize(1);
display.setCursor(110,0);
display.print(curSettings.team2.kurzel);
display.setTextSize(2);
display.setCursor(5,0);
display.print(" ");
if(curSettings.team1.tore<10)
display.print(' ');
display.print(curSettings.team1.tore);
display.print(":");
display.print(curSettings.team2.tore);
display.setCursor(0,18);
display.print("Zeit ");
if(curSettings.spielzeit.minuten<10)
display.print(' ');
display.print(curSettings.spielzeit.minuten);
display.print(":");
display.print(curSettings.spielzeit.sekunden);
display.display();
break;
default:
break;
}
}
void testDisplays()
{
//Standardeinstellungen wurden zuvor initialisiert
aktualisiereDisplay();
}
void funkktionstest()
{
//testNeopixel();//(1)
testDisplays();
//testSound();
}
void aktualisiereFarben()
{
Serial.write(0x0A);
Serial.write(curSettings.team1.teamfarbe.r);
Serial.write(curSettings.team1.teamfarbe.g);
Serial.write(curSettings.team1.teamfarbe.b);
Serial.write(11);
Serial.write(curSettings.team2.teamfarbe.r);
Serial.write(curSettings.team2.teamfarbe.g);
Serial.write(curSettings.team2.teamfarbe.b);
}
//Einstellen der Standardeinstellungen und notwendige Initialisierungen
void initialisieren()
{
_0.r=0;
_0.g=0;
_0.b=0;
_1.r=139;
_1.g=69;
_1.b=19;
_2.r=0;
_2.g=0;
_2.b=255;
_3.r=148;
_3.g=0;
_3.b=211;
_4.r=255;
_4.g=0;
_4.b=0;
_5.r=255;
_5.g=140;
_5.b=0;
_6.r=0;
_6.g=255;
_6.b=0;
_7.r=255;
_7.g=255;
_7.b=0;
_8.r=190;
_8.g=190;
_8.b=190;
_9.r=255;
_9.g=255;
_9.b=255;
Serial.begin(9600);
zeitansagestatus=0;
noansagedelay=0;
flagKommentar=0;
zuletztgepollt=millis();
statusManuelleToranpassung=1;
flagAktualisiereDislpay=0;
curSettings.spielzeit.minuten=10;
curSettings.spielzeit.sekunden=0;
curSettings.kommentatorspruch.minuten=kommentatormin;
curSettings.kommentatorspruch.sekunden=kommentatorsec;
curSettings.werbung.zeitBisNachsteWerbung.minuten=werbedauermin;
curSettings.werbung.zeitBisNachsteWerbung.sekunden=werbedauersec;
curSettings.maxTore=9;
curSettings.spielstatus=0;//Spiel nicht gestartet
curSettings.spielmodus=0;//Modus Torspiel
curSettings.team1.teamname="RED";
curSettings.team1.tore=0;
curSettings.team1.kurzel="R";
curSettings.team1.teamfarbe.r=255;
curSettings.team1.teamfarbe.g=0;
curSettings.team1.teamfarbe.b=0;
curSettings.team2.teamname="BLUE";
curSettings.team2.tore=0;
curSettings.team2.kurzel="B";
curSettings.team2.teamfarbe.r=0;
curSettings.team2.teamfarbe.g=0;
curSettings.team2.teamfarbe.b=255;
aktualisiereFarben();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE);
mp3_1.begin();
mp3_2.begin();
pinMode(taster1,INPUT_PULLUP);
pinMode(taster2,INPUT_PULLUP);
pinMode(taster3,INPUT_PULLUP);
pinMode(taster4,INPUT_PULLUP);
pinMode(taster5,INPUT_PULLUP);
funkktionstest();
noInterrupts(); // Interrupts temporär deaktivieren
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0; // Register mit 0 initialisieren
OCR1A = 62500; // Output Compare Register vorbelegen sodass alle 1s bei einem Takt von 16 MHz bei einem prescaler von 256 ein Interrupt ausgelöst wird
TCCR1B |= (1 << CS12); // 256 als Prescale-Wert spezifizieren
TIMSK1 |= (1 << OCIE1A); // Timer Compare Interrupt aktivieren
interrupts(); // Interrupts scharf schalten
}
//alle 1s ausgelöste ISR um Timer zu dekrementieren und daraus resultierende flags zu setzen
ISR(TIMER1_COMPA_vect)
{
TCNT1 = 0; // Register mit 0 initialisieren
if(curSettings.spielstatus)//Spiel im gange?
{
if(decrementtimer(&curSettings.kommentatorspruch))
{
flagKommentar=1;
}
if(decrementtimer(&curSettings.werbung.zeitBisNachsteWerbung))
{
curSettings.werbung.zustand++;
flagWerbung=1;
}
if(curSettings.spielstatus>1)//Zeitspiel im gange?
{
if(decrementtimer(&curSettings.spielzeit))//wenn das Spiel vorbei ist
{
flagGewinner=1;
curSettings.spielstatus=0;
}
else
{
flagAktualisiereDislpay=1;
}
}
}
}
//dekrementiert den timer und gibt 1 zurück wenn timer abgelaufen (sonst Rückgabewert==0)
uint8_t decrementtimer(timer *curtimer)
{
if(curtimer->sekunden)
{
curtimer->sekunden--;
return 0;
}
else
{
if(curtimer->minuten)
{
curtimer->minuten--;
curtimer->sekunden=59;
return 0;
}
else
{
return 1;
}
}
//Serial.println("debug");
}
//ausgeführt wenn es einen Gewinner gibt
void showGewinner()
{
if(curSettings.team1.tore>curSettings.team2.tore)//hat Team 1 gewonnen?
{
mp3_1.playMp3FolderTrack(2); // sd:/mp3/0002.mp3 //Torjubel
Serial.write(0x07);//(1) //Info für Mikrocontroler 2 für Neopixel
displayGewinner(1);//Oled Display zeigt Sieger an
}
else
{
if(curSettings.team1.tore<curSettings.team2.tore)//hat Team 2 gewonnen?
{
mp3_2.playMp3FolderTrack(2); //Torjubel
Serial.write(0x08);//(1) //Info für Microcontroler 2 für Neopixel
displayGewinner(2);
}
else //Unentschieden
{
Serial.write(0x09);//Info für Microcontroler 2 für Neopixel
displayGewinner(0);
}
}
}
//Zeige auf dem Display an wer gewonnen hat
void displayGewinner(uint8_t team)
{
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0,0);
switch(team)
{
case 0://unentschieden
display.print("Kein Sieger");
break;
case 1://Sieg Team 1
display.print("Gewinner:");
display.setCursor(0,18);
display.print(curSettings.team1.teamname);
break;
case 2://Sieg Team 2
display.print("Gewinner:");
display.setCursor(0,18);
display.print(curSettings.team2.teamname);
break;
}
display.display();
}
//pausiere das Spiel wenn ein Spiel im Gange ist starte/entpausiere es sonst
void startstopp()
{
if(curSettings.spielstatus)
{
curSettings.spielstatus=0;
}
else
{
if(curSettings.spielmodus)
{
curSettings.spielstatus=2;
}
else
{
curSettings.spielstatus=1;
}
}
}
//Überprüfe ob ein Taster gedrückt wurde und führe entsprechende Algorythmen aus
void polleTaster()
{
if(millis()-zuletztgepollt>200)
{
zuletztgepollt=millis();
if(digitalRead(taster1)==LOW)
{
if(statusManuelleToranpassung==1)
{
mp3_2.playMp3FolderTrack(3);
curSettings.team1.tore++;
}
else
{
mp3_2.playMp3FolderTrack(3);
curSettings.team2.tore++;
}
aktualisiereDisplay();
}
else
{
if(digitalRead(taster2)==LOW)
{
if(statusManuelleToranpassung==1)
{
mp3_2.playMp3FolderTrack(3);
curSettings.team1.tore--;
}
else
{
mp3_2.playMp3FolderTrack(3);
curSettings.team2.tore--;
}
aktualisiereDisplay();
}
else
{
if(digitalRead(taster3)==LOW)
{
if(statusManuelleToranpassung==1)
{
statusManuelleToranpassung=2;
mp3_2.playMp3FolderTrack(12);
}
else
{
statusManuelleToranpassung=1;
mp3_1.playMp3FolderTrack(11);
}
}
}
}
if(digitalRead(taster4)==LOW)
{
startstopp();
}
if(digitalRead(taster5)==LOW)
{
startstopp();
}
}
}
//(1)
void werbung()
{
flagWerbung=0;
//Serial.println("Werbung");
Serial.write(0x06);
if(curSettings.werbung.zustand>5)
{
curSettings.werbung.zeitBisNachsteWerbung.minuten=werbedauermin;
curSettings.werbung.zeitBisNachsteWerbung.sekunden=werbedauersec;
curSettings.werbung.zustand=0;
}
}
uint32_t timeout=0;
const uint32_t maxtimeoutmillis=200;
//(1)
void checkapp()
{
if(BTSerial.available())
{
//Serial.println("empfange loopBT");
uint8_t data=BTSerial.read();
timeout=millis();
switch(data)
{
case 0://Teamname Team 1 ändern
curSettings.team1.teamname="";
data=1;
while(data&&millis()-timeout<maxtimeoutmillis)
{
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
{
data=BTSerial.read();
curSettings.team1.teamname+=data;
}
}
break;
case 1://Teamname Team 2 ändern
data=1;
curSettings.team2.teamname="";
while(data&&millis()-timeout<maxtimeoutmillis)
{
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
{
data=BTSerial.read();
curSettings.team2.teamname+=data;
}
}
break;
case 2://Punktestand korrigieren
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);//warte auf empfang eines bytes
if(millis()-timeout<maxtimeoutmillis)
curSettings.team1.tore=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team2.tore=BTSerial.read();
break;
case 3://Teamfarbe Team 1 ändern
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team1.teamfarbe.r=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team1.teamfarbe.g=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team1.teamfarbe.b=BTSerial.read();
break;
case 4://Teamfarbe Team 2 ändern
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team2.teamfarbe.r=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team2.teamfarbe.g=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.team2.teamfarbe.b=BTSerial.read();
break;
case 5://maxToranzahl ändern
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.maxTore=BTSerial.read();
break;
case 6://Spielmodus ändern
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.spielmodus=BTSerial.read();
break;
case 7://Spielzeit ändern
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.spielzeit.minuten=BTSerial.read();
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
curSettings.spielzeit.sekunden=BTSerial.read();
break;
case 8://Spiel pausieren
curSettings.spielstatus=0;
break;
case 9://Spiel fortsetzen
if(curSettings.spielmodus)
{
curSettings.spielstatus=2;
}
else
{
curSettings.spielstatus=1;
}
break;
}
aktualisiereDisplay();
}
}
uint8_t data;
//lese jedes 2te zeichen der seriellen Schnittstelle und füge es dem zurückgegebenen String hinzu
//jedes zweite zeichen da jedem Zeichen das vom 2ten Arduino kommt wenn es über bluetooth empfangen wurde ein byte mit dem wert 0x80 (==128) vorhergeht und ausschliesslich über Bluetooth Strings sendet
String readstring()//nur für Bluetoothdaten
{
String stri="";
data=1;
while(data&&millis()-timeout<maxtimeoutmillis)
{
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
{
Serial.read();//lese 128 aus und verwerfe
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
stri+=(char)Serial.read();
}
}
return stri;
}
//(2)
void readfarbe(farbe *collor)
{
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
Serial.read();//lese 128 aus und verwerfe
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
collor->r=Serial.read();
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
Serial.read();//lese 128 aus und verwerfe
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
collor->g=Serial.read();
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
Serial.read();//lese 128 aus und verwerfe
while(!BTSerial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
collor->b=Serial.read();
}
//gebe von Teamleitung vordefinierte Farbe zurück
farbe getfarbe(uint8_t index)
{
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis){Serial.read();}
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
data=Serial.read();
switch(data)
{
case '1':
return _1;
break;
case '2':
return _2;
break;
case '3':
return _3;
break;
case '4':
return _4;
break;
case '5':
return _5;
break;
case '6':
return _6;
break;
case '7':
return _7;
break;
case '8':
return _8;
break;
case '9':
return _9;
break;
default:
break;
}
}
//uint8_t zwsumme;
//lese jedes zweite byte als char und rechne in uint8 um (z.B. empfange 0x128 0x31(='1') 0x128 0x33(='3') gibt den wert 19 zurück)
uint8_t getHEX()
{
uint8_t zwsumme=0;
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis){Serial.read();}
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
data=Serial.read();
if(data-'0'<10)
{
zwsumme+=(data-'0')*16;
}
else
{
zwsumme+=(data-'A'+10)*16;
}
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis){Serial.read();}
while(!Serial.available()&&millis()-timeout<maxtimeoutmillis);
if(millis()-timeout<maxtimeoutmillis)
data=Serial.read();
...
This file has been truncated, please download it to see its full contents.
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include<Wire.h>
#include <SoftwareSerial.h>
//#include <AltSoftSerial.h>
//definition des Datentyps
struct farbe
{
uint8_t r;
uint8_t g;
uint8_t b;
};
//von der Teamleitung vordefinierte Farben
farbe _0,_1,_2,_3,_4,_5,_6,_7,_8,_9;
uint8_t imGange=0;
//sonstige benötigte Farben
farbe werbeFarbe,farbeTeam1,farbeTeam2;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;// MPU values
const uint32_t maxtimeoutmillis=200;//maximale übertragungszeit der seriellen Schnittstellen (um Funktion nach unvollständigem senden wiederherzustellen
volatile uint8_t flagWerbung;//zeigt an ob der Werbetimer abgelaufen ist
const int MPU_addr=0x68;
const uint16_t numPixels=72;//anzahl der Neopixel im Stripe
const uint8_t pinNeopixel=4;
const uint8_t intPin=2;
const uint8_t pinLichtTor1=5;
const uint8_t pinLichtTor2=6;
const int pinLichtEinwurf=7;
const uint32_t letzteLichtschrankeSperre=1000;//Zeit(ms) nach auslösen einer Lichtschranke in der weiteres Auslösen aus Entprell- und wiederherausspringgründen nicht verarbeiten wird
const int16_t AcXmin=0,AcXmax=2000,AcYmin=-1000,AcYmax=100;//Wertebeireich wann der Tischkicker als "gerade" gilt
uint8_t letzteMessungOk;//beim Auslesen der MPU sind einzelne Ausreißerwerte möglich daher wird das ergebnis ob messsung gerade war oder nicht in dieser Variable gespeichert und nur eine Warnung ausgegeben wenn 2 aufeinanderfolgende Messunger einen schiefen Tisch ergeben
const uint8_t ledTor1min1=0,ledTor1min2=66,ledTor1max1=5,ledTor1max2=71,ledTor2min=30,ledTor2max=41;//index der Neopixel wo die Banden enden/anfangen
uint8_t werbestatus=0;//zeigt beim "durchlaufen" der "Werbung" an wie viele Pixel die Werbung "durchgefahren" ist
const uint8_t werbebreite=4;//wie viele Neopixel ergeben eine Werbeanzeige
uint32_t timeout=0;//wird beim empfan über serielle schnittstelle auf millis gesetzt und mithilfe von millis wird ein Timeout der übertragung realisiert
const uint8_t pinARRX=8,pinARTX=9;
const uint8_t werbeminuten=0;
const uint8_t werbesekunden=10;//werte zum reseten des "Bandenwerbungstimers"
SoftwareSerial ARSerial(pinARRX, pinARTX);
//const uint8_t pinBTRX=A0,pinBTTX=A1;
//SoftwareSerial BTSerial(pinBTRX, pinBTTX);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(numPixels, pinNeopixel, NEO_GRB + NEO_KHZ800);
uint32_t letzteLichtschranke=0;
struct timer{
volatile uint8_t minuten;
volatile uint8_t sekunden;
};
timer werbetimer;
//zeige die Teamfarben über dem Toren an
void aktualisiereFarben()
{
for(int i=ledTor1min1;i<=ledTor1max1;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam1.r,farbeTeam1.g,farbeTeam1.b));
}
for(int i=ledTor1min2;i<=ledTor1max2;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam1.r,farbeTeam1.g,farbeTeam1.b));
}
for(int i=ledTor2min;i<=ledTor2max;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam2.r,farbeTeam2.g,farbeTeam2.b));
}
pixels.show();
}
//gebe die von der Teamleitung vorgegebene Farbe zurück
farbe getfarbe(uint8_t index)
{
switch(index)
{
case '1':
return _1;
break;
case '2':
return _2;
break;
case '3':
return _3;
break;
case '4':
return _4;
break;
case '5':
return _5;
break;
case '6':
return _6;
break;
case '7':
return _7;
break;
case '8':
return _8;
break;
case '9':
return _9;
break;
default:
break;
}
}
//prüfe ob über Bluetooth Daten empfangen wurden und leite sie mit byteweise vorgesendetem byte mit dem Wert 0x80 an den anderen Arduino weiter
// wenn die Daten(wie z.B. Teamfarbe) relevant für diesen sind werden die änderungen übernommen
void checkapp()
{
if(Serial.available())
{
uint8_t tmp=Serial.read();
//Serial.print(String(tmp, HEX));
//uint8_t=
ARSerial.write(128);
ARSerial.write(tmp);
switch(tmp)
{
case 'd':
//curSettings.team1.teamfarbe=getfarbe(data);
while(!Serial.available());
tmp=Serial.read();
ARSerial.write(128);
ARSerial.write(tmp);
farbeTeam1=getfarbe(tmp);
aktualisiereFarben();
break;
case 'D':
//curSettings.team2.teamfarbe=getfarbe(data);
while(!Serial.available());
tmp=Serial.read();
ARSerial.write(128);
ARSerial.write(tmp);
farbeTeam2=getfarbe(tmp);
aktualisiereFarben();
break;
}
}
}
//der nächsten Werbefarbe zufällige Werte zuweisen
void newWerbeFarbe()
{
werbeFarbe.r=random(256);
werbeFarbe.g=random(256);
werbeFarbe.b=random(256);
}
//durch interrupt ausgelöst wenn tor gefallen oder ein Einwurf getätigt wurde Sende die Information an App und anderen Arduino
void lichtschranke()
{
if(millis()-letzteLichtschranke>letzteLichtschrankeSperre)
{
letzteLichtschranke=millis();
if(digitalRead(pinLichtEinwurf))
{
//Serial.println("0x01 Einwurf");
imGange=1;
ARSerial.write(1);
Serial.write(1);
}
if(digitalRead(pinLichtTor1))
{
//Serial.println("0x02 Tor Team 1");
ARSerial.write(2);
Serial.write(2);
}
if(digitalRead(pinLichtTor2))
{
//Serial.println("0x03 Tor Team 2");
ARSerial.write(3);
Serial.write(3);
}
}
}
//für mögliche erweiterungen wo während auf etwas gewartet wird werte geprüft werden sollen
void waitMilliseconds(uint16_t msWait)
{
uint32_t startZeit = millis();
while ((millis() - startZeit) < msWait);
}
//nicht verwendet ursprünglich füt Funktionstest gedacht
void testNeopixel()
{
farbe test;
test.r=255;
test.g=0;
test.b=0;
for(int i=0;i<255;i++)
{
for(int j=0;j<numPixels;j++)
{
pixels.setPixelColor(j,pixels.Color(test.r,test.g,test.b));
}
waitMilliseconds(4);
pixels.show();
//übergang von rot nach blau über die zeit
test.r--;
test.b++;
}
}
//lese die Messwerte der MPU und speichere sie in Variablen
void readMPU()
{
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
}
//gebe zurück ob nach den momentanen MPU-Werten der Tisch als gerade gilt
uint8_t checkMPUval()
{
if(AcX<AcXmin||AcX>AcXmax||AcY<AcYmin||AcY>AcYmax)
{
return 0;
}
else
{
return 1;
}
}
//lasse die "Werbung" eine LED "weiterlaufen"
void werbung()
{
flagWerbung=0;
for(int i=ledTor1max1+1;i<ledTor2min;i+=werbebreite*2)
{
for(int j=0;j<werbestatus;j++)
{
pixels.setPixelColor(i+j,pixels.Color(werbeFarbe.r,werbeFarbe.g,werbeFarbe.b));
}
}
for(int i=ledTor2max+1;i<ledTor1min2;i+=werbebreite*2)
{
for(int j=0;j<werbestatus;j++)
{
pixels.setPixelColor(i+j,pixels.Color(werbeFarbe.r,werbeFarbe.g,werbeFarbe.b));
}
}
pixels.show();
if(werbebreite>werbestatus)//wenn die Werbung noch nicht vollständig durchgelaufen ist
{
werbestatus++;//lasse die Werbung beim nächsten Schritt eine LED "weiterlaufen"
}
else//Werbung vollständig "durchgelaufen" d.h. Timer und Variablen reseten
{
werbestatus=0;
newWerbeFarbe();
werbetimer.minuten=werbeminuten;
werbetimer.sekunden=werbesekunden;
}
}
//(1)
void unentschieden()
{
}
//(1)
void showWinner(farbe teamfarbe)
{
for(int i=ledTor1min1;i<=ledTor1max1;i++)
{
pixels.setPixelColor(i,pixels.Color(teamfarbe.r,teamfarbe.g,teamfarbe.b));
}
for(int i=ledTor1min2;i<=ledTor1max2;i++)
{
pixels.setPixelColor(i,pixels.Color(teamfarbe.r,teamfarbe.g,teamfarbe.b));
}
for(int i=ledTor2min;i<=ledTor2max;i++)
{
pixels.setPixelColor(i,pixels.Color(teamfarbe.r,teamfarbe.g,teamfarbe.b));
}
pixels.show();
}
//(1)
void checkserial(SoftwareSerial qSerial)
{
if(qSerial.available())
{
//Serial.println("ewmpfange");
uint8_t data=qSerial.read();
switch(data)
{
case 0:
testNeopixel();
break;
case 6:
werbung();
break;
case 7:
showWinner(farbeTeam1);
break;
case 8:
showWinner(farbeTeam2);
break;
case 9:
unentschieden();
break;
case 10:
//Serial.print("empfange Farbe ");
timeout=millis();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam1.r=qSerial.read();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam1.g=qSerial.read();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam1.b=qSerial.read();
for(int i=ledTor1min1;i<=ledTor1max1;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam1.r,farbeTeam1.g,farbeTeam1.b));
}
for(int i=ledTor1min2;i<=ledTor1max2;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam1.r,farbeTeam1.g,farbeTeam1.b));
}
pixels.show();
break;
case 11:
timeout=millis();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam2.r=qSerial.read();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam2.g=qSerial.read();
while(!qSerial.available()&&millis()-timeout<maxtimeoutmillis);
farbeTeam2.b=qSerial.read();
for(int i=ledTor2min;i<=ledTor2max;i++)
{
pixels.setPixelColor(i,pixels.Color(farbeTeam2.r,farbeTeam2.g,farbeTeam2.b));
}
pixels.show();
break;
default:
break;
}
}
}
void setup()
{//defaulteinstellung der Teamfarben
farbeTeam1.r=255;
farbeTeam1.g=0;
farbeTeam1.b=0;
farbeTeam2.r=0;
farbeTeam2.g=0;
farbeTeam2.b=255;
//Tisch gilt im ersten Schrtii als gerade
letzteMessungOk=1;
pinMode(intPin,INPUT);
pinMode(pinNeopixel,OUTPUT);
letzteLichtschranke=millis();
//initialisieren der Verbindung mit MPU
Wire.begin();
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
Serial.begin(9600);//starten der Seriellen Schnittstellen
ARSerial.begin(9600);
aktualisiereFarben(); //zeige Teamfarben an ihren Torseiten
attachInterrupt(digitalPinToInterrupt(2),lichtschranke,FALLING); //wird ausgelöst wenn Lichtschranke auslöst
newWerbeFarbe();//Werbefarbe initialisieren
//vordefinierte Farben inizialisieren
_0.r=64;
_0.g=224;
_0.b=208;
_1.r=139;
_1.g=0;
_1.b=0;
_2.r=0;
_2.g=0;
_2.b=255;
_3.r=148;
_3.g=0;
_3.b=211;
_4.r=255;
_4.g=0;
_4.b=0;
_5.r=255;
_5.g=140;
_5.b=0;
_6.r=0;
_6.g=255;
_6.b=0;
_7.r=255;
_7.g=255;
_7.b=0;
_8.r=0;
_8.g=100;
_8.b=0;
_9.r=255;
_9.g=255;
_9.b=255;
werbetimer.minuten=werbeminuten;
werbetimer.sekunden=werbesekunden;//werbetimer initialisieren
//alle 1s Interrupf zum dekrementieren der Timer
noInterrupts(); // Interrupts temporär deaktivieren
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0; // Register mit 0 initialisieren
OCR1A = 62500; // Output Compare Register vorbelegen sodass alle 1s bei einem Takt von 16 MHz bei einem prescaler von 256 ein Interrupt ausgelöst wird
TCCR1B |= (1 << CS12); // 256 als Prescale-Wert spezifizieren
TIMSK1 |= (1 << OCIE1A); // Timer Compare Interrupt aktivieren
interrupts(); // Interrupts scharf schalten
}
//dekrementiert den timer und gibt 1 zurück wenn timer abgelaufen (sonst Rückgabewert==0)
uint8_t decrementtimer(timer *curtimer)
{
if(curtimer->sekunden)
{
curtimer->sekunden--;
return 0;
}
else
{
if(curtimer->minuten)
{
curtimer->minuten--;
curtimer->sekunden=59;
return 0;
}
else
{
return 1;
}
}
//Serial.println("debug");
}
uint32_t letzteSchieflage=0;
const uint32_t schieflagenentprellzeit=1000;
uint8_t flagschief=0;
uint8_t flagzuletztschief=0;
//alle 1s ausgelöst dekrementiert den werbetimer und setzt wenn erforderlich die Werbeflag
ISR(TIMER1_COMPA_vect)
{
TCNT1 = 0; // Register mit 0 initialisieren
if(decrementtimer(&werbetimer))
{
flagWerbung=1;
}
}
//uint32_t letzteSchieflage=0;
void loop()
{
if(flagWerbung)//wenn "Werbeanzeige" weiternachlaufen soll
{
werbung();
}
checkserial(ARSerial);
checkapp();
}










Comments