// ---------------------------------------------------------------- //
// PROJEKT: DART COUNTER v0.4.2 //
// Autor: Twój pomysł, realizacja AI //
// Cel: Poprawka kompilacji - usunięcie duplikatu //
// argumentu domyślnego w funkcji //
// ---------------------------------------------------------------- //
// --- Dołączanie bibliotek ---
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#include <vector> // Używamy wektora do dynamicznej listy graczy
// --- Definicje pinów ---
#define TFT_CS 5
#define TFT_DC 4
#define TFT_RST 2
#define BTN_UP_PIN 13
#define BTN_DOWN_PIN 12
#define BTN_LEFT_PIN 14
#define BTN_RIGHT_PIN 26
#define BTN_OK_PIN 27
#define IR_RECV_PIN 25
// --- Konfiguracja obiektów ---
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
LiquidCrystal_I2C lcd(0x27, 16, 2); // Sprawdź adres I2C swojego modułu (może być 0x3F)
IRrecv irrecv(IR_RECV_PIN);
decode_results results; // Do przechowywania danych z pilota
// --- Stany programu (State Machine) ---
enum ProgramState {
STATE_MAIN_MENU,
STATE_PLAYER_MENU,
STATE_PLAYER_ADD,
STATE_PLAYER_DELETE_CONFIRM,
STATE_REMOTE_MENU,
STATE_REMOTE_LEARN
};
ProgramState currentState = STATE_MAIN_MENU;
// --- Zmienne globalne ---
// Menu główne
const char* mainMenuItems[] = {"GRA", "GRACZE", "UST. PILOTA"};
int mainMenuItemCount = 3;
int selectedMenuItem = 0;
// Gracze
std::vector<String> players;
const int MAX_PLAYERS = 8;
const int MAX_NICK_LENGTH = 10;
int selectedPlayer = 0;
String newPlayerNick = "";
int currentNickCharIndex = 0;
char currentEditableChar = 'A';
// Konfiguracja pilota
struct RemoteButton {
const char* name;
unsigned long code;
};
RemoteButton remoteButtons[] = {
{"DOUBLE", 0}, {"TRIPLE", 0}, {"OK", 0}, {"WSTECZ", 0},
{"GORA", 0}, {"DOL", 0}, {"LEWO", 0}, {"PRAWO", 0},
{"0", 0}, {"1", 0}, {"2", 0}, {"3", 0}, {"4", 0},
{"5", 0}, {"6", 0}, {"7", 0}, {"8", 0}, {"9", 0}
};
int remoteButtonCount = sizeof(remoteButtons) / sizeof(remoteButtons[0]);
int selectedRemoteButton = 0;
int buttonToLearn = -1;
// Zmienne pomocnicze
unsigned long lastButtonPressTime = 0;
const int buttonDebounceDelay = 250;
bool needsRedraw = true;
// --- Deklaracje funkcji (prototypy) ---
void handleMainMenu();
void handlePlayerMenu();
void handlePlayerAdd();
void handleRemoteMenu();
void handleRemoteLearn();
void drawCurrentScreen();
// Argument domyślny jest zdefiniowany TYLKO TUTAJ, w prototypie.
void drawMenuList(const char* title, const char* items[], int itemCount, int selected, bool showStatus = false);
void drawPlayerList();
void drawPlayerAddScreen();
// ================================================================= //
// SETUP //
// ================================================================= //
void setup() {
Serial.begin(115200);
// Inicjalizacja peryferiów
pinMode(BTN_UP_PIN, INPUT_PULLUP);
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
pinMode(BTN_LEFT_PIN, INPUT_PULLUP);
pinMode(BTN_RIGHT_PIN, INPUT_PULLUP);
pinMode(BTN_OK_PIN, INPUT_PULLUP);
tft.begin();
tft.setRotation(1);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("DART COUNTER");
lcd.setCursor(0, 1);
lcd.print("Start...");
irrecv.enableIRIn(); // Uruchomienie odbiornika IR
// Dodajmy kilku graczy na start do testów
players.push_back("JANUSZ");
players.push_back("MARIO");
Serial.println("System gotowy.");
delay(1000);
}
// ================================================================= //
// LOOP //
// ================================================================= //
void loop() {
// Główna pętla obsługuje stany programu
switch (currentState) {
case STATE_MAIN_MENU:
handleMainMenu();
break;
case STATE_PLAYER_MENU:
handlePlayerMenu();
break;
case STATE_PLAYER_ADD:
handlePlayerAdd();
break;
case STATE_REMOTE_MENU:
handleRemoteMenu();
break;
case STATE_REMOTE_LEARN:
handleRemoteLearn();
break;
}
// Rysowanie ekranu tylko gdy jest to konieczne
if (needsRedraw) {
drawCurrentScreen();
needsRedraw = false;
}
}
// ================================================================= //
// FUNKCJE RYSUJĄCE POSZCZEGÓLNE EKRANY //
// ================================================================= //
void drawCurrentScreen() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
switch (currentState) {
case STATE_MAIN_MENU:
drawMenuList("MENU GLOWNE", mainMenuItems, mainMenuItemCount, selectedMenuItem);
break;
case STATE_PLAYER_MENU:
drawPlayerList();
break;
case STATE_PLAYER_ADD:
drawPlayerAddScreen();
break;
case STATE_REMOTE_MENU:
const char* remoteButtonNames[remoteButtonCount];
for(int i=0; i<remoteButtonCount; ++i) remoteButtonNames[i] = remoteButtons[i].name;
drawMenuList("USTAWIENIA PILOTA", remoteButtonNames, remoteButtonCount, selectedRemoteButton, true);
break;
}
}
// POPRAWKA: Usunięto argument domyślny `= false` z definicji funkcji.
void drawMenuList(const char* title, const char* items[], int itemCount, int selected, bool showStatus) {
tft.setTextSize(3);
tft.setCursor(20, 20);
tft.print(title);
tft.setTextSize(2);
int y_start = 60;
int y_step = 25;
int start_index = 0;
int max_items_on_screen = (tft.height() - y_start) / y_step;
if (selected >= max_items_on_screen) {
start_index = selected - max_items_on_screen + 1;
}
for (int i = start_index; i < itemCount; i++) {
int yPos = y_start + (i - start_index) * y_step;
if (yPos > tft.height() - 20) break;
if (i == selected) {
tft.setTextColor(ILI9341_BLACK, ILI9341_YELLOW);
tft.fillRect(10, yPos - 5, tft.width() - 20, 24, ILI9341_YELLOW);
} else {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
tft.setCursor(20, yPos);
tft.print(items[i]);
if(showStatus && remoteButtons[i].code != 0){
tft.setCursor(tft.width() - 60, yPos);
tft.setTextColor(ILI9341_GREEN, (i == selected) ? ILI9341_YELLOW : ILI9341_BLACK);
tft.print("OK");
}
}
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
void drawPlayerList() {
tft.setTextSize(3);
tft.setCursor(20, 20);
tft.print("GRACZE");
tft.setTextSize(2);
if (players.empty()) {
tft.setCursor(20, 60);
tft.print("Brak graczy!");
} else {
for (size_t i = 0; i < players.size(); i++) {
int yPos = 60 + i * 25;
if (i == selectedPlayer) {
tft.setTextColor(ILI9341_BLACK, ILI9341_YELLOW);
tft.fillRect(10, yPos - 5, tft.width() - 20, 24, ILI9341_YELLOW);
} else {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
tft.setCursor(20, yPos);
tft.print(players[i]);
}
}
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(5, tft.height() - 10);
tft.print("<- USUN | DODAJ -> | OK - WSTECZ");
}
void drawPlayerAddScreen() {
tft.setTextSize(3);
tft.setCursor(20, 20);
tft.print("DODAJ GRACZA");
tft.setTextSize(3);
tft.setCursor(20, 80);
tft.print(newPlayerNick);
int cursorX = 20 + (currentNickCharIndex * 18);
tft.setTextColor(ILI9341_BLACK, ILI9341_YELLOW);
tft.setCursor(cursorX, 80);
tft.print(currentEditableChar);
tft.drawFastHLine(cursorX, 80 + 24, 18, ILI9341_YELLOW);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(5, tft.height() - 10);
tft.print("G/D-ZMIEN | OK-ZATW. | L-USUN | P-ZAPISZ");
}
// ================================================================= //
// FUNKCJE OBSŁUGUJĄCE STANY PROGRAMU //
// ================================================================= //
void handleMainMenu() {
if (millis() - lastButtonPressTime < buttonDebounceDelay) return;
if (digitalRead(BTN_DOWN_PIN) == LOW) {
selectedMenuItem = (selectedMenuItem + 1) % mainMenuItemCount;
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_UP_PIN) == LOW) {
selectedMenuItem--;
if (selectedMenuItem < 0) selectedMenuItem = mainMenuItemCount - 1;
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_OK_PIN) == LOW) {
switch (selectedMenuItem) {
case 0: // GRA
break;
case 1: // GRACZE
currentState = STATE_PLAYER_MENU;
break;
case 2: // UST. PILOTA
currentState = STATE_REMOTE_MENU;
break;
}
needsRedraw = true;
lastButtonPressTime = millis();
}
}
void handlePlayerMenu() {
if (millis() - lastButtonPressTime < buttonDebounceDelay) return;
if (digitalRead(BTN_DOWN_PIN) == LOW && !players.empty()) {
selectedPlayer = (selectedPlayer + 1) % players.size();
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_UP_PIN) == LOW && !players.empty()) {
selectedPlayer--;
if (selectedPlayer < 0) selectedPlayer = players.size() - 1;
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_RIGHT_PIN) == LOW) {
if (players.size() < MAX_PLAYERS) {
currentState = STATE_PLAYER_ADD;
newPlayerNick = "";
currentNickCharIndex = 0;
currentEditableChar = 'A';
needsRedraw = true;
}
lastButtonPressTime = millis();
} else if (digitalRead(BTN_LEFT_PIN) == LOW && !players.empty()) {
players.erase(players.begin() + selectedPlayer);
if (selectedPlayer >= players.size() && !players.empty()) {
selectedPlayer = players.size() - 1;
}
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_OK_PIN) == LOW) {
currentState = STATE_MAIN_MENU;
needsRedraw = true;
lastButtonPressTime = millis();
}
}
void handlePlayerAdd() {
if (millis() - lastButtonPressTime < buttonDebounceDelay) return;
if (digitalRead(BTN_UP_PIN) == LOW) {
currentEditableChar++;
if (currentEditableChar > 'Z') currentEditableChar = 'A';
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_DOWN_PIN) == LOW) {
currentEditableChar--;
if (currentEditableChar < 'A') currentEditableChar = 'Z';
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_LEFT_PIN) == LOW) {
if (currentNickCharIndex > 0) {
currentNickCharIndex--;
newPlayerNick.remove(newPlayerNick.length() - 1);
currentEditableChar = 'A'; // Można by przywrócić ostatnią literę
needsRedraw = true;
} else {
currentState = STATE_PLAYER_MENU;
needsRedraw = true;
}
lastButtonPressTime = millis();
} else if (digitalRead(BTN_OK_PIN) == LOW) {
if (currentNickCharIndex < MAX_NICK_LENGTH) {
newPlayerNick += currentEditableChar;
currentNickCharIndex++;
currentEditableChar = 'A';
if (currentNickCharIndex >= MAX_NICK_LENGTH) {
players.push_back(newPlayerNick);
currentState = STATE_PLAYER_MENU;
}
needsRedraw = true;
}
lastButtonPressTime = millis();
} else if (digitalRead(BTN_RIGHT_PIN) == LOW) {
if (newPlayerNick.length() > 0) {
players.push_back(newPlayerNick);
currentState = STATE_PLAYER_MENU;
needsRedraw = true;
}
lastButtonPressTime = millis();
}
}
void handleRemoteMenu() {
if (millis() - lastButtonPressTime < buttonDebounceDelay) return;
if (digitalRead(BTN_DOWN_PIN) == LOW) {
selectedRemoteButton = (selectedRemoteButton + 1) % remoteButtonCount;
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_UP_PIN) == LOW) {
selectedRemoteButton--;
if (selectedRemoteButton < 0) selectedRemoteButton = remoteButtonCount - 1;
needsRedraw = true;
lastButtonPressTime = millis();
} else if (digitalRead(BTN_OK_PIN) == LOW) {
buttonToLearn = selectedRemoteButton;
currentState = STATE_REMOTE_LEARN;
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(20, 100);
tft.print("Wcisnij przycisk na pilocie...");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(remoteButtons[buttonToLearn].name);
lastButtonPressTime = millis();
} else if (digitalRead(BTN_LEFT_PIN) == LOW) {
currentState = STATE_MAIN_MENU;
needsRedraw = true;
lastButtonPressTime = millis();
}
}
void handleRemoteLearn() {
if (irrecv.decode(&results)) {
if (results.value != 0xFFFFFFFF) {
unsigned long receivedCode = results.value;
remoteButtons[buttonToLearn].code = receivedCode;
Serial.print("Zapisano kod dla ");
Serial.print(remoteButtons[buttonToLearn].name);
Serial.print(": 0x");
Serial.println(receivedCode, HEX);
tft.fillScreen(ILI9341_GREEN);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(20, 80);
tft.print("Przycisk zapisany:");
tft.setCursor(20, 110);
tft.setTextSize(3);
tft.print(remoteButtons[buttonToLearn].name);
lcd.setCursor(0, 1);
lcd.print("ZAPISANO OK!");
delay(2000);
currentState = STATE_REMOTE_MENU;
needsRedraw = true;
}
irrecv.resume();
}
}