// 卓球スコアカウンター 改良版スケッチ Ver.36 // TC4028対応、無線受信、DFPlayer音声対応 #include #include #include RF24 radio(7, 8); const byte address[6] = "00001"; uint8_t key = 0; const byte segmentPins[8] = {A0, A1, A2, A3, A4, A5, 2, 3}; const byte bcdPins[3] = {4, 5, 6}; const byte segTable[] = { 0b1111110, 0b0110000, 0b1101101, 0b1111001, 0b0110011, 0b1011011, 0b1011111, 0b1110000, 0b1111111, 0b1111011 }; int scoreA = 0, scoreB = 0; int gamesA = 0, gamesB = 0; bool serveSide = false; bool gameEnded = false; bool displayVisible = true; unsigned long blinkTimer = 0; unsigned int totalPoints = 0; SoftwareSerial mySoftwareSerial(9, 10); DFRobotDFPlayerMini myDFPlayer; void setup() { mySoftwareSerial.begin(9600); myDFPlayer.begin(mySoftwareSerial); myDFPlayer.volume(25); Serial.begin(9600); for (int i = 0; i < 8; i++) pinMode(segmentPins[i], OUTPUT); for (int i = 0; i < 3; i++) pinMode(bcdPins[i], OUTPUT); radio.begin(); radio.setChannel(10); radio.openReadingPipe(0, address); radio.setPALevel(RF24_PA_LOW); radio.startListening(); } void loop() { if (radio.available()) { radio.read(&key, sizeof(key)); handleKey(key); } if (gameEnded) { if (millis() - blinkTimer > 500) { blinkTimer = millis(); displayVisible = !displayVisible; } } else displayVisible = true; if (displayVisible) updateDisplay(); } // ===== キー操作 ===== void handleKey(uint8_t k) { switch (k) { case 1: scoreUp(true); break; case 2: scoreUp(false); break; case 3: resetAll(); break; case 4: endGame(); break; case 5: scoreDown(true); break; case 6: scoreDown(false); break; case 7: changeCourt(); myDFPlayer.playMp3Folder(26); break; // ✅ チェンジコート case 8: serveSide = !serveSide; myDFPlayer.playMp3Folder(30); break; // サーブチェンジ case 9: testAllLED(); break; } } // ===== 得点処理 ===== void scoreUp(bool teamA) { if (gameEnded) return; if (teamA) scoreA++; else scoreB++; totalPoints = scoreA + scoreB; updateServeLogic(); announceScore(); if (checkWin()) { if (teamA) gamesA++; else gamesB++; gameEnded = true; } } void scoreDown(bool teamA) { if (teamA && scoreA > 0) scoreA--; if (!teamA && scoreB > 0) scoreB--; totalPoints = scoreA + scoreB; if (!checkWin()) gameEnded = false; } // ===== 音声アナウンス ===== void announceScore() { if (scoreA >= 10 && scoreB >= 10) { if (scoreA == scoreB) { if (scoreA == 10) { myDFPlayer.playMp3Folder(40); // "テンオール" } else { myDFPlayer.playMp3Folder(29); // "ジュース" } return; } else if (scoreA == scoreB + 1) { myDFPlayer.playMp3Folder(21); // "アドバンテージA" return; } else if (scoreB == scoreA + 1) { myDFPlayer.playMp3Folder(22); // "アドバンテージB" return; } else if (scoreA >= scoreB + 2) { myDFPlayer.playMp3Folder(23); // "Aの勝利" return; } else if (scoreB >= scoreA + 2) { myDFPlayer.playMp3Folder(24); // "Bの勝利" return; } } if ((scoreA == 11 && scoreB <= 9) || (scoreA >= 11 && scoreA - scoreB >= 2 && scoreB < 10)) { myDFPlayer.playMp3Folder(23); // A勝利 return; } if ((scoreB == 11 && scoreA <= 9) || (scoreB >= 11 && scoreB - scoreA >= 2 && scoreA < 10)) { myDFPlayer.playMp3Folder(24); // B勝利 return; } if (scoreA == scoreB && scoreA > 0 && scoreA < 10) { myDFPlayer.playMp3Folder(30 + scoreA); // "ワンオール〜ナインオール" return; } int soundA = getSoundNumber(scoreA); int soundB = getSoundNumber(scoreB); if (soundA > 0) myDFPlayer.playMp3Folder(soundA); delay(600); if (soundB > 0) myDFPlayer.playMp3Folder(soundB); } int getSoundNumber(int score) { if (score == 0) return 20; // ラブ if (score >= 1 && score <= 11) return score; return 0; } // ===== サーブ交代ロジック ===== void updateServeLogic() { static int lastTotalPoints = 0; if (scoreA >= 10 && scoreB >= 10) { if (totalPoints != lastTotalPoints) serveSide = !serveSide; // デュース以降1点ごと交代 } else { if ((totalPoints / 2) != (lastTotalPoints / 2)) serveSide = !serveSide; // 10未満は2点ごと } lastTotalPoints = totalPoints; } bool checkWin() { return ((scoreA >= 11 || scoreB >= 11) && abs(scoreA - scoreB) >= 2); } // ===== 表示処理 ===== void updateDisplay() { for (byte digit = 1; digit <= 5; digit++) { clearDisplay(); setBCD(digit); if (digit == 1) displayNumber((scoreA / 10) % 10, digit); else if (digit == 2) displayNumber(scoreA % 10, digit); else if (digit == 3) displayNumber((scoreB / 10) % 10, digit); else if (digit == 4) displayNumber(scoreB % 10, digit); else if (digit == 5) displayGames(); delayMicroseconds(3000); } } void displayNumber(byte value, byte digit) { byte pattern = segTable[value]; for (int i = 0; i < 7; i++) digitalWrite(segmentPins[i], (pattern >> (6 - i)) & 1 ? LOW : HIGH); digitalWrite(segmentPins[7], (digit == 2 && !serveSide) || (digit == 4 && serveSide) ? LOW : HIGH); } void displayGames() { for (int i = 0; i < 3; i++) digitalWrite(segmentPins[i], (i < gamesA) ? LOW : HIGH); for (int i = 3; i < 6; i++) digitalWrite(segmentPins[i], ((i - 3) < gamesB) ? LOW : HIGH); } void setBCD(byte value) { digitalWrite(bcdPins[0], value & 0x01); digitalWrite(bcdPins[1], (value >> 1) & 0x01); digitalWrite(bcdPins[2], (value >> 2) & 0x01); } void clearDisplay() { for (int i = 0; i < 8; i++) digitalWrite(segmentPins[i], HIGH); } void resetAll() { scoreA = 0; scoreB = 0; gamesA = 0; gamesB = 0; serveSide = false; gameEnded = false; totalPoints = 0; } void endGame() { if (!gameEnded) return; scoreA = 0; scoreB = 0; totalPoints = 0; gameEnded = false; myDFPlayer.playMp3Folder(25); // "ゲームエンド" } void changeCourt() { int tmpScore = scoreA; scoreA = scoreB; scoreB = tmpScore; int tmpGames = gamesA; gamesA = gamesB; gamesB = tmpGames; serveSide = !serveSide; totalPoints = scoreA + scoreB; } void testAllLED() { for (int i = 0; i < 8; i++) digitalWrite(segmentPins[i], LOW); for (byte digit = 1; digit <= 5; digit++) { setBCD(digit); delay(300); } clearDisplay(); }