Leon Glüh
Published

Football table

Modified football table with light and sound effects + automatic points counter

IntermediateFull instructions provided785
Football table

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×2
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×2
Switch Actuator, Head for spring return push-button
Switch Actuator, Head for spring return push-button
×2
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
×1
table football
×1
Matrix Array 1x3
×1
Rocker Switch, Non Illuminated
Rocker Switch, Non Illuminated
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1
DFDPlayer
×1
ir sensor
×1

Story

Read more

Custom parts and enclosures

ball throw

ball removal

display frame

gate frame

platine_final_IX1TfXlT8s.sch

platine_final_2HgKDvDBgR.brd

Schematics

MP3

BRD

created in Adler

sch

created in Adler

Code

Arduino 1

C/C++
#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.

Arduino 2

C/C++
#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();
  }

Credits

Leon Glüh
2 projects • 0 followers

Comments