//#include <MFRC522.h>
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <Adafruit_NeoPixel.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include <TJpg_Decoder.h>
//#include "logo.h"
//#include "ok.h"
//#include "error.h"
#include <FS.h>
#include <LittleFS.h>
#define minimum(a,b) (((a) < (b)) ? (a) : (b))
uint32_t icount = 0;
#define SS_PIN 5
#define RST_PIN 0
#define DATA_LED_PIN 21
#define NUM_PIXELS 22
//MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class
//MFRC522::MIFARE_Key key;
byte nuidPICC[4];
char cssid[23];
const char* ssid = "wokwiguest";
const char* password = "";
const char* apiEndpoint = "http://arcadecontrol.eu/api/";
int price = 0;
int loopCheck = 2;
int readerBlock = 0;
int ScreenIs = 0;
int screenRotation = 1;
int newScreenRotation = 1;
int colorType = 1;
int colorR = 0;
int colorG = 0;
int colorB = 255;
int ledwidth = 1;
int leddelay = 100;
#define minimum(a,b) (((a) < (b)) ? (a) : (b))
TFT_eSPI tft = TFT_eSPI();
Adafruit_NeoPixel ws2812b(NUM_PIXELS, DATA_LED_PIN, NEO_GRB + NEO_KHZ800);
unsigned long previousMillis = 0; // für nicht-blockierende Delays
unsigned long lastUpdateMillis = 0;
void setup() {
Serial.begin(9600);
// Initialisiere TFT Display
tftSetup();
// Starte LEDs
ledSetup();
// Initialisiere RFID Reader
rfidSetup();
// Verbinde mit WiFi
wifiSetup();
// Download Images
downloadImages();
// TJpg_Decoder initialisieren
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(tft_output);
}
void loop() {
// Überprüfe regelmäßig die WiFi-Verbindung
checkWiFiConnection();
// Hauptlogik
mainLogic();
}
void tftSetup() {
tft.begin();
tft.setRotation(screenRotation);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
tft.setTextSize(1);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Booting please wait...", 0, 0, 2);
newScreenRotation = 0;
delay(100);
tft.drawString("Initialis FileSystem ...", 0, 0, 2);
if (!LittleFS.begin(true)) {
Serial.println("Fehler beim Initialisieren von LittleFS!");
return;
}
Serial.println("Formatiere LittleFS...");
LittleFS.format();
Serial.println("LittleFS formatiert.");
}
void ledSetup() {
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, ws2812b.Color(255, 0, 0));
ws2812b.show();
delay(100);
}
}
void rfidSetup() {
pinMode(32, OUTPUT);
pinMode(14, OUTPUT);
pinMode(27, OUTPUT);
pinMode(26, OUTPUT);
pinMode(25, OUTPUT);
pinMode(33, OUTPUT);
/*tft.drawString("Inilazie RFID Reader Please Wait...", 0, 0, 2);
SPI.begin(); // Init SPI bus
rfid.PCD_Init(); // Init MFRC522
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
Serial.println(F("This code scan the MIFARE Classsic NUID."));
Serial.print(F("Using the following key:"));
printHex(key.keyByte, MFRC522::MF_KEY_SIZE);
delay(100);*/
}
void downloadImages() {
tft.setTextSize(1);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
// Initialer Download-Status
tft.drawString("Downloading Images Please wait ... 0%", 0, 0, 2);
delay(100);
// Erste Datei herunterladen
String url = String(apiEndpoint) + "/ok.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/ok.jpg"); // Hier sollte es vermutlich "/ok.jpg" statt "/error.jpg" sein
tft.drawString("Downloading Images Please wait ... 20%", 0, 0, 2);
delay(100);
delay(5000);
// Zweite Datei herunterladen
url = String(apiEndpoint) + "/logo.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/logo.jpg");
tft.drawString("Downloading Images Please wait ... 60%", 0, 0, 2);
delay(100);
delay(5000);
// Dritte Datei herunterladen
url = String(apiEndpoint) + "/error.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/error.jpg");
tft.drawString("Downloading Images Please wait ... 100%", 0, 0, 2);
delay(100);
}
void wifiSetup() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
Serial.println(WiFi.localIP());
Serial.print(F("Reader :"));
snprintf(cssid, 23, "ARCADE-%llX", ESP.getEfuseMac());
Serial.println("CHIP SSID");
Serial.println(cssid);
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Booting successfull. Reader ID:", 0, 0, 2);
tft.drawString(cssid, 0, 16, 2);
}
void mainLogic() {
unsigned long currentMillis = millis();
ws2812b.setBrightness(255);
if (currentMillis - previousMillis >= 1000) {
previousMillis = currentMillis;
if (currentMillis - lastUpdateMillis >= 60000 || lastUpdateMillis == 0) {
lastUpdateMillis = currentMillis;
updateReaderInfo();
}
}
if (readerBlock == 0) {
if(colorType == 1) {
animateLEDsOneColor(ws2812b.Color(colorR, colorG, colorB));
}
if(colorType == 2) {
animateLEDsRainbow();
}
if(colorType == 3) {
animateLEDsPulse(ws2812b.Color(colorR, colorG, colorB));
}
if(colorType == 4) {
animateLEDBlink(ws2812b.Color(colorR, colorG, colorB), leddelay);
}
if(colorType == 5) {
animateLEDRunningLight(ws2812b.Color(colorR, colorG, colorB), ledwidth, leddelay);
}
if(colorType == 6) {
animateLEDWave(ws2812b.Color(colorR, colorG, colorB), ledwidth, leddelay);
}
if(colorType == 7) {
animateLEDTwinkle(ws2812b.Color(colorR, colorG, colorB), ledwidth, leddelay);
}
} else {
ChangeTFT(4, "");
setAllLEDsColor(ws2812b.Color(255, 0, 0));
delay(1000);
}
}
void checkWiFiConnection() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected. Attempting to reconnect...");
WiFi.disconnect();
WiFi.reconnect();
}
}
void updateReaderInfo() {
JSONVar jsonReturn = apiCall("getReaderInfo.php?readerid=" + String(cssid));
int reboot = jsonReturn["reboot"];
if(reboot == 1) {
ESP.restart();
}
price = jsonReturn["price"];
newScreenRotation = jsonReturn["screenRotation"];
colorType = jsonReturn["color"]["type"];
colorR = jsonReturn["color"]["color"]["r"];
colorG = jsonReturn["color"]["color"]["g"];
colorB = jsonReturn["color"]["color"]["b"];
ledwidth = jsonReturn["color"]["width"];
leddelay = jsonReturn["color"]["delay"];
Serial.println(colorType);
if (JSON.stringify(jsonReturn["blocked"]) == "\"noblock\"") {
readerBlock = 0;
} else {
readerBlock = 1;
}
}
void animateLEDsOneColor(uint32_t color) {
ChangeTFT(1, String(price));
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
checkRFID();
ws2812b.setPixelColor(pixel, color);
ws2812b.show();
delay(100);
}
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
checkRFID();
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 0, 0));
ws2812b.show();
delay(100);
}
}
void animateLEDsRainbow() {
for(int i = 0; i < 256; i++) { // 256 Schritte für das vollständige Regenbogenspektrum
ChangeTFT(1, String(price));
checkRFID();
for(int pixel = 0; pixel < NUM_PIXELS; pixel++) {
int shiftedPosition = (i + pixel) % 256; // Verschiebe die Position für den Regenbogeneffekt
ws2812b.setPixelColor(pixel, Wheel(shiftedPosition));
}
ws2812b.show();
}
}
void animateLEDsPulse(uint32_t color) {
for(int i = 0; i <= 255; i++) { // 256 Schritte für das vollständige Regenbogenspektrum
ChangeTFT(1, String(price));
checkRFID();
for(int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, color);
ws2812b.setBrightness(i);
}
Serial.println(i);
ws2812b.show();
}
for(int i = 255; i >= 0; i--) { // 256 Schritte für das vollständige Regenbogenspektrum
ChangeTFT(1, String(price));
checkRFID();
for(int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, color);
ws2812b.setBrightness(i);
}
Serial.println(i);
ws2812b.show();
}
}
void animateLEDBlink(uint32_t color, uint32_t delayTime) {
for (int round = 0; round < 2; round++) {
ChangeTFT(1, String(price));
checkRFID();
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
if (pixel % 2 == round) {
ws2812b.setPixelColor(pixel, color);
} else {
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 0, 0));
}
}
ws2812b.show();
delay(delayTime);
}
}
void animateLEDRunningLight(uint32_t color, int width, uint32_t delayTime) {
for (int round = 0; round < NUM_PIXELS; round++) {
ChangeTFT(1, String(price));
checkRFID();
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
// Berechne die relative Position im "laufenden Fenster"
int relativePos = (pixel + NUM_PIXELS - round) % NUM_PIXELS;
// Überprüfe, ob die aktuelle LED im "laufenden Fenster" liegt
if (relativePos < width) {
ws2812b.setPixelColor(pixel, color);
} else {
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 0, 0));
}
}
ws2812b.show();
delay(delayTime);
}
}
void animateLEDWave(uint32_t color, int waveLength, uint32_t delayTime) {
int brightness;
for (int position = 0; position < NUM_PIXELS + waveLength; position++) {
ChangeTFT(1, String(price));
checkRFID();
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
int distance = abs(pixel - position);
if (distance < waveLength) {
brightness = 255 - (distance * 255 / waveLength);
uint32_t dimmedColor = dimColor(color, brightness);
ws2812b.setPixelColor(pixel, dimmedColor);
} else {
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 0, 0));
}
}
ws2812b.show();
delay(delayTime);
}
}
void animateLEDTwinkle(uint32_t color, int length, uint32_t delayTime) {
setAllLEDsColor(ws2812b.Color(0, 0, 0)); // Schaltet alle LEDs aus
for (int i = 0; i < length; i++) {
ChangeTFT(1, String(price));
checkRFID();
int pixel = random(NUM_PIXELS);
ws2812b.setPixelColor(pixel, color);
ws2812b.show();
delay(delayTime);
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 0, 0)); // Schaltet die LED wieder aus
}
ws2812b.show();
}
uint32_t dimColor(uint32_t color, int brightness) {
int red = (color >> 16) & 0xFF;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
red = (red * brightness) / 255;
green = (green * brightness) / 255;
blue = (blue * brightness) / 255;
return ws2812b.Color(red, green, blue);
}
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return ws2812b.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return ws2812b.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return ws2812b.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
void setAllLEDsColor(uint32_t color) {
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, color);
}
ws2812b.show();
}
void checkRFID(void) {
/*
if ( ! rfid.PICC_IsNewCardPresent())
return;
if ( ! rfid.PICC_ReadCardSerial())
return;
Serial.print(F("PICC type: "));
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
Serial.println(rfid.PICC_GetTypeName(piccType));
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
Serial.println(F("A new card has been detected."));
for (byte i = 0; i < 4; i++) {
nuidPICC[i] = rfid.uid.uidByte[i];
}
Serial.println(F("The NUID tag is:"));
Serial.print(F("In hex: "));
printHex(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
Serial.print(F("In dec: "));
printDec(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
JSONVar jsonReturn = apiCall("/checkCard.php?cardId=" + String(returnDec(rfid.uid.uidByte,rfid.uid.size)) + "&readerid=" + String(cssid));
Serial.println(jsonReturn["status"]);
if(JSON.stringify(jsonReturn["status"]) == "\"ok\"") {
cardok(jsonReturn["pin"], jsonReturn["impuls"], JSON.stringify(jsonReturn["rememberCredits"]));
} else {
cardnotok();
}
*/
}
void cardok(int pin, int impuls, String remeberCredits) {
ChangeTFT(2, String(remeberCredits));
ws2812b.setBrightness(255);
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, ws2812b.Color(0, 255, 0));
}
ws2812b.show();
for(int i = 0; i < impuls; i++) {
digitalWrite(pin, HIGH);
delay(200);
digitalWrite(pin, LOW);
delay(200);
}
delay(1000);
}
void cardnotok() {
ChangeTFT(3, "");
ws2812b.setBrightness(255);
for (int pixel = 0; pixel < NUM_PIXELS; pixel++) {
ws2812b.setPixelColor(pixel, ws2812b.Color(255, 0, 0));
}
ws2812b.show();
delay(1000);
}
/**
* Helper routine to dump a byte array as hex values to Serial.
*/
void printHex(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
/**
* Helper routine to dump a byte array as dec values to Serial.
*/
void printDec(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], DEC);
}
}
String returnDec(byte *buffer, byte bufferSize) {
String cid = "";
for (byte i = 0; i < bufferSize; i++) {
cid = cid + (buffer[i] < 0x10 ? " 0" : "-");
cid = cid + buffer[i], DEC;
}
return cid;
}
JSONVar apiCall(String end) {
HTTPClient http; //Declare object of class HTTPClient
String host = String(apiEndpoint) + String(end);
Serial.print("Request Link:");
Serial.println(host);
http.begin(host); //Specify request destination
int httpCode = http.GET(); //Send the request
String payload = http.getString(); //Get the response payload from server
Serial.print("Response Code:"); //200 is OK
Serial.println(httpCode); //Print HTTP return code
Serial.print("Returned data from Server:");
Serial.println(payload); //Print request response payload
JSONVar jsonReturn = JSON.parse(payload);
http.end();
return jsonReturn;
}
void ChangeTFT(int Screen, String text) {
if(screenRotation != newScreenRotation) {
screenRotation = newScreenRotation;
tft.setRotation(screenRotation);
tft.fillScreen(TFT_BLACK);
ScreenIs = 0;
}
if(Screen == 1 && Screen != ScreenIs) {
//Standart
tft.setTextSize(1);
tft.fillScreen(TFT_WHITE);
if (!LittleFS.exists("/logo.jpg")) {
String url = String(apiEndpoint) + "/logo.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/logo.jpg");
delay(5000);
}
drawJpeg("/logo.jpg");
tft.setTextColor(TFT_BLACK, TFT_WHITE);
tft.drawString("Credits per Play: " + text, 5, 5, 2);
//rfid.PCD_Init();
ScreenIs = 1;
}
if(Screen == 2 && Screen != ScreenIs) {
//OK
/*tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Play Card OK Have Fun", 0, 0, 2);
tft.drawString("Remeber Credits: " + text, 0, 16, 2);*/
tft.fillScreen(TFT_WHITE);
if (!LittleFS.exists("/ok.jpg")) {
String url = String(apiEndpoint) + "/ok.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/ok.jpg");
delay(5000);
}
drawJpeg("/ok.jpg");
tft.setTextSize(2);
tft.setTextColor(TFT_BLACK, TFT_WHITE);
tft.drawString(text, 140, 190, 2);
//rfid.PCD_Init();
ScreenIs = 2;
}
if(Screen == 3 && Screen != ScreenIs) {
//Error
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Play Card Not Ok Please Try agian", 0, 0, 2);
tft.fillScreen(TFT_WHITE);
if (!LittleFS.exists("/error.jpg")) {
String url = String(apiEndpoint) + "/error.jpg?readerid=" + String(cssid);
downloadAndSaveImage(url.c_str(), "/error.jpg");
delay(5000);
}
drawJpeg("/error.jpg");
//rfid.PCD_Init();
ScreenIs = 3;
}
if(Screen == 4 && Screen != ScreenIs) {
//Error
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Reader Out of Service", 0, 0, 2);
//rfid.PCD_Init();
ScreenIs = 4;
}
}
void downloadAndSaveImage(const char* url, const char* path) {
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) { // HTTP-Statuscode 200 OK
File file = LittleFS.open(path, "w");
if (file) {
http.writeToStream(&file);
file.close();
Serial.println("Bild heruntergeladen und gespeichert");
} else {
Serial.println("Fehler beim Öffnen der Datei");
}
} else {
Serial.print("Fehler beim Herunterladen des Bildes: ");
Serial.println(http.errorToString(httpCode));
}
http.end();
}
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {
if ( y >= tft.height() ) return false;
tft.pushImage(x, y, w, h, bitmap);
return true;
}
void drawJpeg(const char *filename) {
TJpgDec.drawFsJpg(0, 0, filename,LittleFS);
}
void leereLittleFS() {
File root = LittleFS.open("/", FILE_READ);
if (!root) {
Serial.println("Fehler beim Öffnen des Dateisystems!");
return;
}
if (!root.isDirectory()) {
Serial.println("Kein Verzeichnis gefunden!");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print("Verzeichnis gefunden: ");
Serial.println(file.name());
} else {
Serial.print("Lösche Datei: ");
Serial.println(file.name());
LittleFS.remove(file.name());
}
file = root.openNextFile();
}
}