Main CT-1 V3

Program Utama innoiti CT1 (Tinggal ganti SSID)

main_v3.ino INO FILE
#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// Pin definitions for ESP32  (disarankan)
#define RST_PIN     27    // Reset pin untuk RC522
#define SS_PIN      5     // SS/CS RC522
#define BUZZER_PIN  4     // Buzzer
#define LED_PIN     16    // LED indikator

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
MFRC522::StatusCode status;

// WiFi configuration
const char* ssid = "TPQ Jabal Rohmah";
const char* password = "lamongan";
const char* serverUrl = "http://zidcreative.com/innoiti/cust/cust_6/php/absensi.php";

// NTP Client configuration
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 25200, 60000); // GMT+7 (25200 seconds)

// Variables for attendance tracking
int dailyAttendanceCount = 0;
unsigned long currentAttendanceDay = 0;
float currentSpeedKBps = 0;
unsigned long lastSpeedTestTime = 0;
const long speedTestInterval = 10000; // Measure speed every 10 seconds

void setup() {
  Serial.begin(9600);
  SPI.begin();
  
  // Initialize RFID reader
  mfrc522.PCD_Init();
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;  // Default key
  
  // Initialize OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.clearDisplay();
  display.setFont(&FreeSansBold12pt7b);
  display.setTextColor(WHITE);
  display.setCursor(5, 20);
  display.println("Innoiti");
  display.setCursor(5, 45);
  display.println("CT-1");
  display.display();

  // Initialize buzzer and LED
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(LED_PIN, LOW);

  // Play intro sound
  playIntro();

  // Connect to WiFi
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Initialize NTP client
  timeClient.begin();
}

void loop() {
  measureNetworkSpeed();
  timeClient.update();

  // Display time and status
  displayTime();

  // Check for RFID card
  if (!mfrc522.PICC_IsNewCardPresent()) return;
  if (!mfrc522.PICC_ReadCardSerial()) return;

  Serial.println("\nReading RFID data...");
  handleRFIDCard();
  
  // Halt communication with card
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

void handleRFIDCard() {
  displayProcessing();
  
  // Read data from blocks
  byte buffer[18];
  byte bufferSize = sizeof(buffer);
  
  String id_jari = "Unknown";
  String name = "Unknown";
  String kode = "Unknown";

  // Read ID from block 5
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 5, &key, &(mfrc522.uid));
  if (status == MFRC522::STATUS_OK) {
    status = mfrc522.MIFARE_Read(5, buffer, &bufferSize);
    if (status == MFRC522::STATUS_OK) {
      id_jari = "";
      for (byte i = 0; i < 16; i++) {
        if (buffer[i] != 0x00) id_jari += (char)buffer[i];
      }
      id_jari.trim(); // Remove any whitespace
    }
  }

  // Read name from block 4
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(mfrc522.uid));
  if (status == MFRC522::STATUS_OK) {
    status = mfrc522.MIFARE_Read(4, buffer, &bufferSize);
    if (status == MFRC522::STATUS_OK) {
      name = "";
      for (byte i = 0; i < 16; i++) {
        if (buffer[i] != 0x00) name += (char)buffer[i];
      }
      name.trim(); // Remove any whitespace
    }
  }

  // Read kode from block 6
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 6, &key, &(mfrc522.uid));
  if (status == MFRC522::STATUS_OK) {
    status = mfrc522.MIFARE_Read(6, buffer, &bufferSize);
    if (status == MFRC522::STATUS_OK) {
      kode = "";
      for (byte i = 0; i < 16; i++) {
        if (buffer[i] != 0x00) kode += (char)buffer[i];
      }
      kode.trim(); // Remove any whitespace
    }
  }

  // Validate data
  if (id_jari.length() == 0 || id_jari == "Unknown" || 
      name.length() == 0 || name == "Unknown" ||
      kode.length() == 0 || kode == "Unknown") {
    Serial.println("Invalid card data");
    displayError("Ups Gagal");
    playErrorSound();
    return;
  }

  // Check if kode is "A"
  if (kode != "A") {
    Serial.println("Kartu ditolak - Kode tidak valid");
    displayError("Tertolak");
    playErrorSound();
    return;
  }

  Serial.print("ID: "); Serial.println(id_jari);
  Serial.print("Name: "); Serial.println(name);
  Serial.print("Kode: "); Serial.println(kode);

  // Handle attendance
  unsigned long currentTime = timeClient.getEpochTime();
  unsigned long currentDay = currentTime / 86400; // Days since 1 Jan 1970

  // Reset counter if new day
  if (currentDay != currentAttendanceDay) {
    dailyAttendanceCount = 0;
    currentAttendanceDay = currentDay;
  }

  // Process attendance
  dailyAttendanceCount++;
  sendData(id_jari, name);
}

void sendData(String id_jari, String name) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi not connected! Trying to reconnect...");
    WiFi.disconnect();
    WiFi.reconnect();
    delay(5000);
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("Failed to connect to WiFi");
      displayError("WiFi Error!");
      return;
    }
  }

  HTTPClient http;
  http.begin(serverUrl);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");

  String postData = "id_jari=" + id_jari + "&name=" + name;
  
  int httpResponseCode = http.POST(postData);
  
  if (httpResponseCode > 0) {
    Serial.print("Data sent successfully! HTTP Response: ");
    Serial.println(httpResponseCode);
    displaySuccess(name.c_str());
    playSuccessSound();
  } else {
    Serial.println("Failed to send data");
    displayError("Coba Lagi");
    playErrorSound();
  }

  http.end();
}

void playIntro() {
  digitalWrite(LED_PIN, HIGH);
  tone(BUZZER_PIN, 1000, 200);
  delay(200);
  tone(BUZZER_PIN, 1200, 200);
  delay(200);
  tone(BUZZER_PIN, 1500, 300);
  delay(300);
  noTone(BUZZER_PIN);
  digitalWrite(LED_PIN, LOW);
}

void playSuccessSound() {
  for (int i = 0; i < 2; i++) {
    digitalWrite(BUZZER_PIN, HIGH);
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    digitalWrite(BUZZER_PIN, LOW);
    digitalWrite(LED_PIN, LOW);
    delay(100);
  }
}

void playErrorSound() {
  digitalWrite(BUZZER_PIN, HIGH);
  digitalWrite(LED_PIN, HIGH);
  delay(1000);
  digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(LED_PIN, LOW);
}

void displayTime() {
  display.clearDisplay();

  // Display organization name
  display.setFont(&FreeSerif9pt7b);
  display.setTextColor(WHITE);
  display.setCursor(2, 14);
  display.println("- Jabal Rahmah -");

  // Display time
  display.setFont(&FreeSansBold12pt7b);
  display.setCursor(17, 42);
  display.println(timeClient.getFormattedTime());

  // Additional functions (WiFi indicator and counter)
  drawWifiIndicator();
  drawAttendanceCounter();

  display.display();
}

void displaySuccess(const char* message) {
  display.clearDisplay();
  
  // Animated circle
  for(int i=0; i<=360; i+=30) {
    display.clearDisplay();
    drawAnimatedCircle(64, 20, 10, i);
    display.display();
    delay(20);
  }
  
  // Animated checkmark
  drawAnimatedCheckmark(64, 20);
  
  // Success message
  display.setFont(&FreeSansBold9pt7b);
  display.setTextColor(WHITE);
  
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(message, 0, 0, &x1, &y1, &w, &h);
  
  display.setCursor((SCREEN_WIDTH - w)/2, 50);
  display.println(message);
  
  display.drawFastHLine(10, 55, SCREEN_WIDTH-20, WHITE);
  
  display.display();
}

void drawAnimatedCircle(int16_t x, int16_t y, int16_t r, int16_t angle) {
  float rad = angle * PI / 180;
  int16_t x2 = x + r * cos(rad);
  int16_t y2 = y + r * sin(rad);
  display.drawLine(x, y, x2, y2, WHITE);
}

void drawAnimatedCheckmark(int16_t x, int16_t y) {
  display.fillCircle(x, y, 12, WHITE);
  
  for(int i=0; i<=10; i++) {
    display.fillCircle(x, y, 12, WHITE);
    
    int16_t x1 = x - 8;
    int16_t y1 = y;
    int16_t x2 = x - 2;
    int16_t y2 = y + 6;
    int16_t drawX2 = x1 + (x2 - x1) * i / 10;
    int16_t drawY2 = y1 + (y2 - y1) * i / 10;
    display.drawLine(x1, y1, drawX2, drawY2, BLACK);
    
    if(i > 5) {
      int16_t x3 = x2;
      int16_t y3 = y2;
      int16_t x4 = x + 8;
      int16_t y4 = y - 6;
      int16_t drawX4 = x3 + (x4 - x3) * (i-5) / 5;
      int16_t drawY4 = y3 + (y4 - y3) * (i-5) / 5;
      display.drawLine(x3, y3, drawX4, drawY4, BLACK);
    }
    
    display.display();
    delay(30);
  }
}

void drawWifiIndicator() {
  int rssi = WiFi.RSSI();
  int bars;
  
  if (rssi > -55) bars = 4;
  else if (rssi > -65) bars = 3;
  else if (rssi > -75) bars = 2;
  else if (rssi > -85) bars = 1;
  else bars = 0;

  int x = 7;
  int y = 61;
  
  display.fillRect(x, y-10, 30, 12, BLACK);
  
  display.drawPixel(x, y-2, WHITE);
  display.drawPixel(x+1, y-2, WHITE);
  display.drawPixel(x+2, y-3, WHITE);
  display.drawPixel(x+3, y-3, WHITE);
  display.drawPixel(x+4, y-4, WHITE);
  display.drawPixel(x+5, y-4, WHITE);
  
  for (int i = 0; i < bars; i++) {
    display.fillRect(x + 8 + (i*5), y - 2 - (i*2), 3, 2 + (i*2), WHITE);
  }
}

void drawAttendanceCounter() {
  display.setFont(NULL);
  display.setTextSize(1);
  
  // Display network speed
  display.setCursor(42, 55);
  display.print(currentSpeedKBps, 1);
  display.print("KB/s");

  // Display attendance counter
  display.setCursor(100, 55);
  display.print("A:");
  display.print(dailyAttendanceCount);
}

void measureNetworkSpeed() {
  if (millis() - lastSpeedTestTime >= speedTestInterval) {
    HTTPClient http;
    const char* testUrl = "http://httpbin.org/stream-bytes/1024";
    long fileSize = 1024;
    
    http.begin(testUrl);
    int httpCode = http.GET();
    
    if (httpCode == HTTP_CODE_OK) {
      long startTime = millis();
      WiFiClient *stream = http.getStreamPtr();
      while (stream->available()) {
        stream->read();
      }
      long duration = millis() - startTime;
      
      if (duration > 0) {
        currentSpeedKBps = (fileSize / 1024.0) / (duration / 1000.0);
        Serial.print("Speed: ");
        Serial.print(currentSpeedKBps);
        Serial.println(" KB/s");
      }
    }
    http.end();
    lastSpeedTestTime = millis();
  }
}

void displayProcessing() {
  display.clearDisplay();
  
  static float angle = 0;
  const int centerX = SCREEN_WIDTH/2;
  const int centerY = SCREEN_HEIGHT/2 - 5;
  const int radius = 12;
  
  display.drawCircle(centerX, centerY, radius, WHITE);
  
  int dotX = centerX + radius * cos(angle);
  int dotY = centerY + radius * sin(angle);
  display.fillCircle(dotX, dotY, 2, WHITE);
  
  angle += 0.2;
  if(angle > 2*PI) angle = 0;
  
  display.setFont(&FreeSansBold9pt7b);
  display.setTextColor(WHITE);
  
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds("Memproses", 0, 0, &x1, &y1, &w, &h);
  display.setCursor((SCREEN_WIDTH - w)/2, centerY + radius + 20);
  display.println("Memproses");
  
  display.display();
}

void displayError(String errorMsg) {
  display.clearDisplay();
  display.setFont(&FreeSansBold12pt7b);
  display.setTextColor(WHITE);

  display.setCursor(0, 40);
  display.println(errorMsg);
  display.display();
}