//#include "DYPlayerArduino.h" //Audio Player
#include <FastLED.h> //programmable LED Strip
#include <Bounce2.h> //Momentary Buttons
#include <movingAvg.h> //Moving Average calculation
Bounce2::Button oButton_LH = Bounce2::Button();
Bounce2::Button oButton_RH = Bounce2::Button();
Bounce2::Button oFunk_1 = Bounce2::Button();
Bounce2::Button oFunk_2 = Bounce2::Button();
Bounce2::Button oBack = Bounce2::Button();
Bounce2::Button oReset = Bounce2::Button();
//#define ARDUINO_RX 3//should connect to TX of the Serial MP3 Player module
//#define ARDUINO_TX 2//connect to RX of the module
// How many LEDs are used in each digit
#define NUM_LEDS_PER_DIGIT 15
// Total number of LEDs in the strip
#define NUM_LEDS_1 69 //TBC
#define NUM_LEDS_2 52 //TBC
// The pin which is connected to the DataIn of the LED strip
#define LED_Strip_1 17 //A3
#define LED_Strip_2 18 //A4
// Pin Definitions
const int P15 = 2;
const int P25 = 3;
const int Button_LH = 4;
const int Button_RH = 5;
const int LED_LH = 6;
const int LED_RH = 7;
const int B_Reset = 8;
const int B_Funk1 = 9;
const int B_Funk2 = 10;
const int MP3_Busy = 11;
// 12 free
// 13 free
const int SoundMin = 14; // A0
const int SoundMax = 15; // A1
const int B_Back = 16; // A2
// 17 reserved above (A3)
// 18 reserved above (A4)
const int Poti = 19; // A5
// 20 (A6) free
// 21 (A7) free
int iScore[] = {0,0};
int iSets[] = {0,0};
int iLastPoint = -1;
int iRotation = -1;
int iBackup[] = {0,0,0,0};
int PointsPerSet = 25;
int PotiValue;
int iSoundChoice = 1;
int SoundLvl;
char FilePath[7];
// Storage for temporary value when switching sides
bool bTmpVal;
int iTmpVal;
// Variables for indicating rotation by flashing Button
bool bLEDstate=LOW;
long lastLEDchange;
// Variables for reminding players to push buttons for points
int iTimeLastPoint = -1;
int iPointDurAvg = -1;
movingAvg PointDuration(5); // Number of data points to take into consideration for averaging
bool bSetClosed=LOW;
//byte sendBuffer[6]; //buffer that will be used to store commands before sending
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
//Initialize MP3 Player
//DY::Player player;
const uint32_t digits1[10] = {
0b000111111111111000, // 0
0b000110000000011000, // 1
0b000111100111100110, // 2
0b000111100001111110, // 3
0b000110011000011110, // 4
0b000001111001111110, // 5
0b000001111111111110, // 6
0b000111100000011000, // 7
0b000111111111111110, // 8
0b000111111001111110, // 9
};
const uint32_t digits2[10] = {
0b000001111111111110, // 0
0b000000000111100000, // 1
0b000111111001111000, // 2
0b000110011111111000, // 3
0b000110000111100110, // 4
0b000110011110011110, // 5
0b000111111110011110, // 6
0b000000000111111000, // 7
0b000111111111111110, // 8
0b000110011111111110, // 9
};
CRGB leds_1[NUM_LEDS_1];
CRGB leds_2[NUM_LEDS_2];
uint8_t colorIndex[NUM_LEDS_2];
DEFINE_GRADIENT_PALETTE (volleyball_gp) {
0, 26, 113, 237,
84, 224, 231, 239,
168, 232, 194, 32,
255, 26, 113, 237
};
CRGBPalette16 volleyball = volleyball_gp;
void setup() {
// Serial interface for debugging
//Serial.begin(115200);
Serial.begin(9600);
//Serial.print(__FILE__ __DATE__);
//player.begin();
delay(500);//allow everything to settle down
//player.setCycleMode(DY::PlayMode::OneOff);
//PointDuration.begin();
for (int i =0; i < NUM_LEDS_2; i++) {
colorIndex[i] = 255/NUM_LEDS_2 * i;
}
FastLED.addLeds<WS2812B, LED_Strip_1, GRB>(leds_1, NUM_LEDS_1);
FastLED.addLeds<WS2812B, LED_Strip_2, GRB>(leds_2, NUM_LEDS_2);
FastLED.setCorrection(TypicalLEDStrip); //Alternative: 'UncorrectedColor' or 'TypicalPixelString'
FastLED.setBrightness(255);
// Colorize points initially
fill_solid(leds_1,NUM_LEDS_1,CHSV(171,255,255)); // Point digits
leds_1[NUM_LEDS_PER_DIGIT*4 + 2] = CHSV(150,255,255); // Dot between points 1
leds_1[NUM_LEDS_PER_DIGIT*4 + 3] = CHSV(150,255,255); // Dot between points 2
FastLED.show();
// Define inputs and outputs
pinMode(LED_LH, OUTPUT);
pinMode(LED_RH, OUTPUT);
pinMode(P15, INPUT_PULLUP);
pinMode(P25, INPUT_PULLUP);
pinMode(SoundMin, INPUT_PULLUP);
pinMode(SoundMax, INPUT_PULLUP);
pinMode(MP3_Busy, INPUT_PULLUP);
oButton_LH.attach( Button_LH, INPUT_PULLUP);
oButton_LH.interval(5);
oButton_LH.setPressedState(LOW);
oButton_RH.attach( Button_RH, INPUT_PULLUP);
oButton_RH.interval(5);
oButton_RH.setPressedState(LOW);
oFunk_1.attach( B_Funk1, INPUT_PULLUP);
oFunk_1.interval(5);
oFunk_1.setPressedState(LOW);
oFunk_2.attach( B_Funk2, INPUT_PULLUP);
oFunk_2.interval(5);
oFunk_2.setPressedState(LOW);
oBack.attach( B_Back, INPUT_PULLUP);
oBack.interval(5);
oBack.setPressedState(LOW);
oReset.attach( B_Reset, INPUT_PULLUP);
oReset.interval(5);
oReset.setPressedState(LOW);
lastLEDchange=millis();
// Initial light show after powering up the system
for (int i=0; i<300; i++){
fadeToBlackBy(leds_1,NUM_LEDS_1,10);
int pos = random16(NUM_LEDS_1);
leds_1[pos] += CHSV(100 + random8(48),200,255);
FastLED.show();
delay(10);
}
for (int fadeStep=0; fadeStep<255; fadeStep+=5) {
fadeToBlackBy(leds_1, NUM_LEDS_1, fadeStep);
FastLED.show();
delay(10);
}
Serial.println("Ready");
}
void loop() {
// Determine intended sound volume
PotiValue = analogRead(Poti);
SoundLvl = map(PotiValue,0,1023,0,30);
//player.setVolume(SoundLvl);
oButton_LH.update();
oButton_RH.update();
oFunk_1.update();
oFunk_2.update();
oBack.update();
oReset.update();
// Determine until how many points it will be played in this set
if (digitalRead(P15) == LOW) {
PointsPerSet = 15; // Manual setting for 15 points per set
}
else if (digitalRead(P25) == LOW) {
PointsPerSet = 25; // Manual setting for 25 points per set
}
else {
// Auto mode: 25 points per set or 15 in the 5th set
PointsPerSet = 25;
if (iSets[0] == 2 && iSets[1] == 2) {
PointsPerSet = 15;
}
}
// Determine which kind of sound setup is selected
if (digitalRead(SoundMin) == LOW) {
iSoundChoice = 0; // Minimum sounds selected
}
else if (digitalRead(SoundMax) == LOW) {
iSoundChoice = 2; // Maximum sounds selected (stupid commentator mode)
}
else {
iSoundChoice = 1; // Standard sounds selected
}
// Point for LH team
if ((oButton_LH.pressed() || oFunk_1.pressed()) && bSetClosed == LOW) {
Serial.println("Point");
PointScored(0);
Serial.println(iScore[0]);
}
// Point for RH team
if ((oButton_RH.pressed() || oFunk_2.pressed()) && bSetClosed == LOW) {
PointScored(1);
}
// Indicate rotation and serve
if (iRotation >= 0) {
if(millis()-lastLEDchange >= 400) {
if (iRotation == 0) {
//digitalWrite(LED_LH,!digitalRead(LED_LH));
}
else if (iRotation == 1) {
//digitalWrite(LED_RH,!digitalRead(LED_RH));
}
lastLEDchange=millis();
}
}
// Check whether reminder about pressing buttons is required
if ((millis()-iTimeLastPoint) > (0.8 * iPointDurAvg) && iTimeLastPoint > -1) {
for (int i =0; i < NUM_LEDS_2; i++) {
//leds_2[i] = ColorFromPalette(volleyball, colorIndex[i]);
}
EVERY_N_MILLISECONDS(map(iPointDurAvg,0.8*iPointDurAvg,1.2*iPointDurAvg,1000,20)) {
for (int i =0; i < NUM_LEDS_2; i++) {
//colorIndex[i]++;
}
}
FastLED.show();
}
else {
//fadeToBlackBy(leds_2, NUM_LEDS_2,10);
}
// Check if LH side is winning this set
if ((iScore[0] >= PointsPerSet) && (iScore[0] > iScore[1] + 1) && bSetClosed == LOW) {
iSets[0] += 1;
bSetClosed = HIGH;
}
if ((iScore[1] >= PointsPerSet) && (iScore[1] > iScore[0] + 1) && bSetClosed == LOW) {
iSets[1] += 1;
bSetClosed = HIGH;
}
if (bSetClosed == HIGH) {
SetFinished();
}
// Back button for when he last point shall be made undone
if (oBack.pressed()) {
Serial.println("back");
ReadBackup();
bSetClosed = LOW;
}
// Reset button to reset the current set and proceed to the next one or reset the complete match (long press)
if (oReset.pressed()) {
Serial.println("reset");
ResetSet();
}
if (oReset.isPressed() && oReset.currentDuration() >= 2000) {
ResetGame();
Serial.println("Reset game");
}
}
//Actions to be performed after scoring a point
void PointScored(int iSide) {
// iSide = 0 means LH side scored
// iSide = 1 means RH side scored
// Create Backup
//WriteBackup();
// Track time it took between this point and the last one to feed reminder animation
//if (iScore[0] + iScore[1] >= 1) {
//iPointDurAvg = PointDuration.reading(millis()-iTimeLastPoint);
//}
iTimeLastPoint = millis();
// Acustic confirmation that point has been registered
//WaitForMP3();
//player.playSpecifiedDevicePath(DY::Device::Sd,"/bing.mp3");
// Animation confirmation that point hat been registered
for (int i = 0; i<=12; i++) {
if (iSide == 0) {
leds_2[i+26] = CHSV(145,255,255);
leds_2[51-i] = CHSV(145,255,255);
}
else if (iSide == 1) {
leds_2[i] = CHSV(145,255,255);
leds_2[25-i] = CHSV(145,255,255);
}
fadeToBlackBy(leds_2, NUM_LEDS_2, 10);
FastLED.show();
//delay(50);
}
// Show current standing
iScore[iSide] = iScore[iSide] + 1;
Serial.println(iScore[iSide]);
//ShowPoints();
// Indicate side for next server
//digitalWrite(LED_LH, iSide==0);
//digitalWrite(LED_RH, iSide==1);
// Announce current standing if applicable
//if (((iScore[0] + iScore[1]) % 5 == 0) || ((iScore[0] >= 20 || iScore[1] >= 20) && iSoundChoice >= 1)) {
//String TmpPath = "/" + String(iScore[iSide]) + ".mp3";
//TmpPath.toCharArray(FilePath, sizeof(FilePath));
//WaitForMP3();
//player.playSpecifiedDevicePath(DY::Device::Sd,FilePath);
//TmpPath = "/" + String(iScore[!iSide]) + ".mp3";
//TmpPath.toCharArray(FilePath, sizeof(FilePath));
//WaitForMP3();
//delay(50);
//player.playSpecifiedDevicePath(DY::Device::Sd,FilePath);
// --------------- SET BALL
// ----------------MATCH BALL
//}
// Check for rotation = change in serve
//iRotation = -1;
//if (iLastPoint != iSide && iLastPoint >= 0) {
//iRotation = iSide;
//}
//iLastPoint = iSide;
Serial.println(iScore[iSide]);
Serial.println(iScore[0]);
}
// Wait until MP3 player is ready again
void WaitForMP3() {
while (digitalRead(MP3_Busy)==LOW) {
delay(50);
}
}
// Actions to be performed after set is won
void SetFinished() {
// Show winning half in green and loosing half in red
uint8_t iSinBeat = beatsin8(30,50,255,0,0);
for (int i = 0; i<=25; i++) {
if (iScore[0] < iScore[1] ) {
leds_2[i] = CHSV(96,255,iSinBeat);
leds_2[i+26] = CHSV(0,255,iSinBeat);
}
else {
leds_2[i] = CHSV(0,255,iSinBeat);
leds_2[i+26] = CHSV(96,255,iSinBeat);
}
}
FastLED.show();
// Show current standing in sets via LED Strip
ShowSets();
// Play cheers sound if applicable
if (iSoundChoice >= 1) {
//WaitForMP3();
//player.playSpecifiedDevicePath(DY::Device::Sd,"/cheers.mp3");
}
}
// Write current standing into backup paraneter
void WriteBackup() {
iBackup[0] = iScore[0];
iBackup[1] = iScore[1];
iBackup[2] = iSets[0];
iBackup[3] = iSets[1];
}
// Read current standing from backup paraneter
void ReadBackup() {
iScore[0] = iBackup[0];
iScore[1]= iBackup[1];
ShowPoints();
iSets[0]= iBackup[2];
iSets[1]= iBackup[3];
ShowSets();
}
// Reset current standing to move to next set
void ResetSet() {
setDigit1(0, 0, CHSV(171, 255, 255));
setDigit2(1, 0, CHSV(171, 255, 255));
setDigit1(2, 0, CHSV(171, 255, 255));
setDigit2(3, 0, CHSV(171, 255, 255));
FastLED.show();
iScore[0] = 0;
iScore[1] = 0;
iRotation = -1;
iLastPoint = -1;
bSetClosed=LOW;
//Switch sides
iTmpVal = iSets[0];
iSets[0] = iSets[1];
iSets[1] = iTmpVal;
ShowSets();
bTmpVal = digitalRead(LED_LH);
digitalWrite(LED_LH,digitalRead(LED_RH));
digitalWrite(LED_RH,bTmpVal);
}
// Reset Game, which means that also the number of won sets is resetted
void ResetGame() {
ResetSet();
iSets[0] = 0;
iSets[1] = 0;
ShowSets();
digitalWrite(LED_RH, LOW);
digitalWrite(LED_LH, LOW);
}
// Set the values in the LED strip corresponding to a particular display/value
void setDigit1(int display, int val, CHSV colour) {
for(int i=0;i<NUM_LEDS_PER_DIGIT; i++){
colour.v = bitRead(digits1[val], i) * 255; //255 is brigthness
leds_1[display*NUM_LEDS_PER_DIGIT + i] = colour;
}
}
// Set the values in the LED strip corresponding to a particular display/value
void setDigit2(int display, int val, CHSV colour){
for(int i=0;i<NUM_LEDS_PER_DIGIT; i++){
colour.v = bitRead(digits2[val], i) * 255; //255 is brigthness
leds_2[display*NUM_LEDS_PER_DIGIT + i] = colour;
}
}
// Displays the number of achieved points for each team via LED strip
void ShowPoints() {
setDigit1(0, (iScore[0]/10)%10, CHSV(map(iScore[0],0,PointsPerSet,0,85), 255, 255));
setDigit2(1, iScore[0]%10, CHSV(map(iScore[0],0,PointsPerSet,0,85), 255, 255));
setDigit1(2, (iScore[1]/10)%10, CHSV(map(iScore[1],0,PointsPerSet,0,85), 255, 255));
setDigit2(3, iScore[1]%10, CHSV(map(iScore[1],0,PointsPerSet,0,85), 255, 255));
FastLED.show();
}
// Displays the number of won sets for each team via LED strip
void ShowSets() {
for (int i = 1; i <= 3; i++) {
leds_1[NUM_LEDS_PER_DIGIT*4 + 2 + i] = CHSV(150,255,iSets[0]>=i);
}
for (int i = 1; i <= 3; i++) {
leds_1[NUM_LEDS_PER_DIGIT*4 + 5 + i] = CHSV(150,255,iSets[1]>=i);
}
FastLED.show();
}
void FlashLED(int iPinLED, int interval) {
// Examples: interval of 500 is slow, interval of 100 is fast flashing
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(iPinLED, ledState);
}
}