#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
// NFCNFCNFC
#include <Wire.h>
#include <Adafruit_PN532.h>
// END NFCFCNFC
// OLED display width and height
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
// NFCNFCNFC
#define SDA_PIN 21 // Change if using a different pin
#define SCL_PIN 22 // Change if using a different pin
#define I2C_ADDRESS 0x24 // Default address of PN532
// end NFCNFCNFC
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// NFCNFCNFC
Adafruit_PN532 nfc(SDA_PIN, SCL_PIN);
// END NFCFCNFC
// Define button pins
const int buttonPark = 12;
const int buttonRide = 14;
const int buttonLand = 27;
// Park and ride selection variables
int currentPark = 0;
int currentRide = 0;
int currentLand = 0;
bool parkChanged = false;
// Button state tracking
bool lastButtonParkState = HIGH;
bool lastButtonRideState = HIGH;
bool lastButtonLandState = HIGH;
// Debounce delay in milliseconds
const unsigned long DEBOUNCE_DELAY = 50;
// Park API URLs
const char* parks[] = {
"https://queue-times.com/en-US/parks/6/queue_times.json",
"https://queue-times.com/en-US/parks/5/queue_times.json",
"https://queue-times.com/en-US/parks/8/queue_times.json",
"https://queue-times.com/en-US/parks/7/queue_times.json"
};
const char* parkNames[] = { "MK", "EP", "AK", "HS" };
// WiFi setup
const char* apSSID = "Setup-AP";
const char* apPassword = "123456789";
// Default WiFi credentials
const char* defaultSSID = "wifi2g";
const char* defaultPassword = "qwerty12";
WebServer server(80);
// Define EEPROM addresses
const int SSID_ADDR = 0;
const int PASSWORD_ADDR = 32;
// Timer variables
unsigned long buttonPressTime = 0;
unsigned long lastUpdateTime = 0;
const unsigned long DISPLAY_DURATION = 5000;
const unsigned long UPDATE_INTERVAL = 300000;
bool displayingWaitTime = false;
int currentWaitTime = 0;
DynamicJsonDocument parkData(8192);
// Forward declarations for functions
void displayError(const char* message);
void displayWaitTimes();
void updateDisplay();
int getRideCount();
int getLandCount();
void displayStartupMessage();
void handleRoot();
void handleWiFiSetup();
void saveWiFiCredentials(String ssid, String password);
void setup() {
Serial.begin(115200);
Serial.println("Hello!");
EEPROM.begin(512);
// NFCNFCNFC
// Initialize I2C communication
Wire.begin(SDA_PIN, SCL_PIN);
// Initialize the PN532
nfc.begin();
// Configure PN532 to read RFID tags
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) {
Serial.print("Could not detect PN532. Please check wiring.");
while (1);
}
Serial.println("Found PN532!");
// Configure PN532 to read RFID tags
nfc.SAMConfig();
Serial.println("Waiting for an NFC card...");
// end NFCNFCNFC
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("SSD1306 allocation failed");
for (;;);
}
displayStartupMessage();
pinMode(buttonPark, INPUT_PULLUP);
pinMode(buttonRide, INPUT_PULLUP);
pinMode(buttonLand, INPUT_PULLUP);
// Setup Wi-Fi
WiFi.softAP(apSSID, apPassword);
server.on("/", HTTP_GET, handleRoot);
server.on("/setup", HTTP_POST, handleWiFiSetup);
server.begin();
Serial.println("HTTP server started");
// Try to connect to Wi-Fi using stored credentials or default credentials
String storedSSID = EEPROM.readString(SSID_ADDR);
String storedPassword = EEPROM.readString(PASSWORD_ADDR);
if (storedSSID.length() > 0 && storedPassword.length() > 0) {
WiFi.begin(storedSSID.c_str(), storedPassword.c_str());
Serial.print("Connecting to ");
Serial.print(storedSSID);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected to WiFi");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Connected to ");
display.println(storedSSID);
display.setCursor(0, 16);
display.println("Press park selection button");
display.display();
} else {
Serial.println("Failed to connect to WiFi. Connecting with default credentials.");
WiFi.begin(defaultSSID, defaultPassword);
int defaultAttempts = 0;
while (WiFi.status() != WL_CONNECTED && defaultAttempts < 20) {
delay(500);
Serial.print(".");
defaultAttempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected to WiFi with default credentials");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Connected to ");
display.println(defaultSSID);
display.setCursor(0, 16);
display.println("Press park selection button");
display.display();
} else {
Serial.println("Failed to connect to WiFi");
displayError("No Wi-Fi");
}
}
} else {
Serial.println("No Wi-Fi credentials stored. Connecting with default credentials.");
WiFi.begin(defaultSSID, defaultPassword);
int defaultAttempts = 0;
while (WiFi.status() != WL_CONNECTED && defaultAttempts < 20) {
delay(500);
Serial.print(".");
defaultAttempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected to WiFi with default credentials");
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Connected to ");
display.println(defaultSSID);
display.setCursor(0, 16);
display.println("Press park selection button");
display.display();
} else {
Serial.println("Failed to connect to WiFi");
displayError("No Wi-Fi");
}
}
lastUpdateTime = millis();
}
void loop() {
server.handleClient();
unsigned long currentTime = millis();
// Button Handling
bool parkButtonState = digitalRead(buttonPark);
if (parkButtonState == LOW && lastButtonParkState == HIGH && (currentTime - buttonPressTime >= DEBOUNCE_DELAY)) {
currentPark = (currentPark + 1) % 4;
currentRide = 0;
currentLand = 0;
parkChanged = true;
displayWaitTimes();
buttonPressTime = currentTime;
displayingWaitTime = true;
lastUpdateTime = currentTime;
}
lastButtonParkState = parkButtonState;
bool rideButtonState = digitalRead(buttonRide);
if (rideButtonState == LOW && lastButtonRideState == HIGH && (currentTime - buttonPressTime >= DEBOUNCE_DELAY)) {
currentRide = (currentRide + 1) % getRideCount();
parkChanged = true;
displayWaitTimes();
buttonPressTime = currentTime;
displayingWaitTime = true;
lastUpdateTime = currentTime;
}
lastButtonRideState = rideButtonState;
bool landButtonState = digitalRead(buttonLand);
if (landButtonState == LOW && lastButtonLandState == HIGH && (currentTime - buttonPressTime >= DEBOUNCE_DELAY)) {
currentLand = (currentLand + 1) % getLandCount();
parkChanged = true;
displayWaitTimes();
buttonPressTime = currentTime;
displayingWaitTime = true;
lastUpdateTime = currentTime;
}
lastButtonLandState = landButtonState;
// Update Display
if (currentTime - lastUpdateTime >= (displayingWaitTime ? DISPLAY_DURATION : UPDATE_INTERVAL)) {
updateDisplay();
lastUpdateTime = currentTime;
}
// NFC Reading
static unsigned long lastNFCCheckTime = 0;
if (currentTime - lastNFCCheckTime >= 500) { // Check NFC every 500 ms
lastNFCCheckTime = currentTime;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength;
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
Serial.println("Found an NFC card!");
Serial.print("UID Length: "); Serial.print(uidLength, DEC); Serial.println(" bytes");
Serial.print("UID Value: ");
for (uint8_t i = 0; i < uidLength; i++) {
Serial.print(" 0x"); Serial.print(uid[i], HEX);
}
Serial.println("");
}
}
}
void handleRoot() {
server.send(200, "text/html", R"(
<!DOCTYPE html>
<html>
<body>
<h1>WiFi Setup</h1>
<form action="/setup" method="post">
SSID: <input type="text" name="ssid"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Save">
</form>
</body>
</html>
)");
}
void handleWiFiSetup() {
String ssid = server.arg("ssid");
String password = server.arg("password");
saveWiFiCredentials(ssid, password);
server.send(200, "text/html", "<h1>WiFi credentials saved. Please restart the device.</h1>");
}
void saveWiFiCredentials(String ssid, String password) {
EEPROM.writeString(SSID_ADDR, ssid);
EEPROM.writeString(PASSWORD_ADDR, password);
EEPROM.commit();
}
void displayError(const char* message) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(message);
display.display();
}
void displayWaitTimes() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(parks[currentPark]);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println("Received JSON payload:");
Serial.println(payload);
deserializeJson(parkData, payload);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(parkNames[currentPark]);
JsonArray lands = parkData["lands"].as<JsonArray>();
if (currentLand < lands.size()) {
JsonObject land = lands[currentLand].as<JsonObject>();
JsonArray rides = land["rides"].as<JsonArray>();
if (currentRide < rides.size()) {
JsonObject ride = rides[currentRide].as<JsonObject>();
Serial.println("Ride object:");
serializeJson(ride, Serial);
Serial.println();
if (ride.containsKey("wait_time")) {
currentWaitTime = ride["wait_time"].as<int>();
} else {
Serial.println("No 'waitTime' field found in ride object");
currentWaitTime = 0;
}
display.setCursor(0, 16);
display.print("Land: ");
display.println(land["name"].as<String>());
display.setCursor(0, 32);
display.print("Ride: ");
display.println(ride["name"].as<String>());
display.setCursor(0, 48);
display.print("Wait Time: ");
display.print(currentWaitTime);
display.print(" min");
display.display();
} else {
displayError("Ride Error");
}
} else {
displayError("Land Error");
}
} else {
displayError("Hot Dog");
}
http.end();
} else {
displayError("No Wi-Fi");
}
}
void updateDisplay() {
if (displayingWaitTime) {
display.clearDisplay();
display.setTextSize(6);
display.setTextColor(SSD1306_WHITE);
String waitTimeStr = String(currentWaitTime);
int16_t x1, y1;
uint16_t textWidth, textHeight;
display.getTextBounds(waitTimeStr, 0, 0, &x1, &y1, &textWidth, &textHeight);
int x = (SCREEN_WIDTH - textWidth) / 2;
int yOffset = 4;
int y = (SCREEN_HEIGHT - textHeight) / 2 + yOffset;
display.setCursor(x, y);
display.print(currentWaitTime);
display.display();
} else {
displayWaitTimes();
}
}
int getRideCount() {
if (currentLand < parkData["lands"].size()) {
JsonArray rides = parkData["lands"][currentLand]["rides"].as<JsonArray>();
return rides.size();
}
return 0;
}
int getLandCount() {
return parkData["lands"].size();
}
void displayStartupMessage() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Magic Forge Wait Times");
display.display();
delay(2000);
}
its very strange that it works fine if a NFC is tapped and remains tapped