Program ESP32 untuk Multiplexer 64 Channel dengan FIFO dan Web Interface

Program ESP32  untuk Multiplexer 64 Channel dengan FIFO dan Web Interface + LCD:


```cpp

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <SD.h>

#include <SPI.h>

#include <WiFi.h>

#include <WebServer.h>

#include <ArduinoJson.h>


// Konfigurasi Hardware

#define MUX_S0 13

#define MUX_S1 12

#define MUX_S2 14

#define MUX_S3 27

#define MUX_SIG 36

#define SD_CS 5


// Konfigurasi LCD I2C

#define LCD_ADDRESS 0x27

#define LCD_COLS 16

#define LCD_ROWS 2


// Konfigurasi Threshold

#define ACTIVE_THRESHOLD 2000

#define DEBOUNCE_TIME 100


// Konfigurasi FIFO

#define MAX_FIFO_ENTRIES 10


// Konfigurasi WiFi

const char* ssid = "YourSSID";

const char* password = "YourPassword";


// Struktur Data Channel

struct Channel {

  int id;

  String name;

  int value;

  bool active;

  unsigned long timestamp;

};


// FIFO

Channel fifo[MAX_FIFO_ENTRIES];

int fifoCount = 0;


// Variabel Global

Channel channels[64];

WebServer server(80);

LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLS, LCD_ROWS);


void setup() {

  Serial.begin(115200);

  

  // Inisialisasi Pin

  pinMode(MUX_S0, OUTPUT);

  pinMode(MUX_S1, OUTPUT);

  pinMode(MUX_S2, OUTPUT);

  pinMode(MUX_S3, OUTPUT);

  pinMode(MUX_SIG, INPUT);

  

  // Inisialisasi LCD

  lcd.init();

  lcd.backlight();

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("Initializing...");

  

  // Inisialisasi SD Card

  if (!SD.begin(SD_CS)) {

    lcd.clear();

    lcd.print("SD Card Failed");

    while (1);

  }

  

  // Baca file channels.csv

  if (!loadChannelNames()) {

    lcd.clear();

    lcd.print("Channel CSV Error");

    while (1);

  }

  

  // Hubungkan ke WiFi

  WiFi.begin(ssid, password);

  lcd.clear();

  lcd.print("Connecting WiFi");

  

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    lcd.print(".");

  }

  

  lcd.clear();

  lcd.print("WiFi Connected");

  lcd.setCursor(0, 1);

  lcd.print(WiFi.localIP());

  delay(2000);

  

  // Setup Web Server

  setupWebServer();

  

  // Tampilkan konfigurasi sistem

  printSystemConfig();

}


void loop() {

  server.handleClient();

  

  // Baca semua channel

  readAllChannels();

  

  // Update FIFO

  updateFifo();

  

  // Update LCD

  updateLcd();

  

  // Kirim data FIFO ke Serial jika perlu

  if (Serial.available()) {

    if (Serial.read() == 'f') {

      sendFifoToSerial();

    }

  }

  

  delay(50);

}


// Fungsi untuk membaca nilai dari multiplexer

int readMux(int channel) {

  digitalWrite(MUX_S0, bitRead(channel, 0));

  digitalWrite(MUX_S1, bitRead(channel, 1));

  digitalWrite(MUX_S2, bitRead(channel, 2));

  digitalWrite(MUX_S3, bitRead(channel, 3));

  return analogRead(MUX_SIG);

}


// Baca semua 64 channel

void readAllChannels() {

  for (int i = 0; i < 64; i++) {

    channels[i].value = readMux(i);

    channels[i].active = (channels[i].value > ACTIVE_THRESHOLD);

    channels[i].timestamp = millis();

  }

}


// Update FIFO dengan data channel aktif

void updateFifo() {

  // Hapus entri yang tidak aktif lagi

  for (int i = 0; i < fifoCount; i++) {

    int channelId = fifo[i].id;

    if (!channels[channelId].active) {

      // Geser semua entri setelahnya ke depan

      for (int j = i; j < fifoCount - 1; j++) {

        fifo[j] = fifo[j + 1];

      }

      fifoCount--;

      i--; // Periksa lagi posisi ini karena ada pergeseran

    }

  }

  

  // Tambahkan channel aktif baru ke FIFO

  for (int i = 0; i < 64; i++) {

    if (channels[i].active) {

      bool alreadyInFifo = false;

      

      // Cek apakah channel sudah ada di FIFO

      for (int j = 0; j < fifoCount; j++) {

        if (fifo[j].id == i) {

          alreadyInFifo = true;

          // Update nilai dan timestamp

          fifo[j].value = channels[i].value;

          fifo[j].timestamp = channels[i].timestamp;

          break;

        }

      }

      

      // Jika belum ada di FIFO dan masih ada slot

      if (!alreadyInFifo && fifoCount < MAX_FIFO_ENTRIES) {

        fifo[fifoCount].id = i;

        fifo[fifoCount].name = channels[i].name;

        fifo[fifoCount].value = channels[i].value;

        fifo[fifoCount].active = true;

        fifo[fifoCount].timestamp = channels[i].timestamp;

        fifoCount++;

      }

    }

  }

  

  // Urutkan FIFO berdasarkan timestamp (terlama pertama)

  sortFifoByTimestamp();

}


// Urutkan FIFO berdasarkan timestamp

void sortFifoByTimestamp() {

  for (int i = 0; i < fifoCount - 1; i++) {

    for (int j = i + 1; j < fifoCount; j++) {

      if (fifo[i].timestamp > fifo[j].timestamp) {

        Channel temp = fifo[i];

        fifo[i] = fifo[j];

        fifo[j] = temp;

      }

    }

  }

}


// Load nama channel dari SD card

bool loadChannelNames() {

  File file = SD.open("/channels.csv");

  if (!file) {

    return false;

  }

  

  while (file.available()) {

    String line = file.readStringUntil('\n');

    line.trim();

    

    int commaPos = line.indexOf(',');

    if (commaPos == -1) continue;

    

    String idStr = line.substring(0, commaPos);

    String name = line.substring(commaPos + 1);

    

    int id = idStr.toInt();

    if (id >= 0 && id < 64) {

      channels[id].id = id;

      channels[id].name = name;

    }

  }

  

  file.close();

  return true;

}


// Update tampilan LCD

void updateLcd() {

  static unsigned long lastUpdate = 0;

  if (millis() - lastUpdate < 1000) return;

  lastUpdate = millis();

  

  // Hitung channel aktif

  int activeChannels = 0;

  for (int i = 0; i < 64; i++) {

    if (channels[i].active) activeChannels++;

  }

  

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("Active: ");

  lcd.print(activeChannels);

  

  lcd.setCursor(0, 1);

  lcd.print("FIFO: ");

  lcd.print(fifoCount);

}


// Setup Web Server

void setupWebServer() {

  server.on("/", HTTP_GET, []() {

    String html = "<html><head><title>ESP32 Multiplexer Monitor</title>";

    html += "<style>table {border-collapse: collapse;} th, td {border: 1px solid #ddd; padding: 8px;}</style>";

    html += "</head><body>";

    html += "<h1>Active Channels</h1>";

    html += "<table><tr><th>ID</th><th>Name</th><th>Value</th><th>Active</th><th>Timestamp</th></tr>";

    

    for (int i = 0; i < fifoCount; i++) {

      html += "<tr>";

      html += "<td>" + String(fifo[i].id) + "</td>";

      html += "<td>" + fifo[i].name + "</td>";

      html += "<td>" + String(fifo[i].value) + "</td>";

      html += "<td>" + String(fifo[i].active ? "Yes" : "No") + "</td>";

      html += "<td>" + String(fifo[i].timestamp) + "</td>";

      html += "</tr>";

    }

    

    html += "</table>";

    html += "<h2>Upload New channels.csv</h2>";

    html += "<form method='POST' action='/upload' enctype='multipart/form-data'>";

    html += "<input type='file' name='csvfile' accept='.csv'>";

    html += "<input type='submit' value='Upload'>";

    html += "</form>";

    html += "<h2>Download Current channels.csv</h2>";

    html += "<a href='/download'>Download channels.csv</a>";

    html += "</body></html>";

    

    server.send(200, "text/html", html);

  });

  

  server.on("/download", HTTP_GET, []() {

    File file = SD.open("/channels.csv");

    if (file) {

      server.sendHeader("Content-Type", "text/csv");

      server.sendHeader("Content-Disposition", "attachment; filename=channels.csv");

      server.streamFile(file, "text/csv");

      file.close();

    } else {

      server.send(404, "text/plain", "File not found");

    }

  });

  

  server.on("/upload", HTTP_POST, []() {

    server.send(200, "text/plain", "Upload complete. Please restart the device.");

  }, []() {

    HTTPUpload& upload = server.upload();

    if (upload.status == UPLOAD_FILE_START) {

      SD.remove("/channels.csv");

      File file = SD.open("/channels.csv", FILE_WRITE);

      file.close();

    } else if (upload.status == UPLOAD_FILE_WRITE) {

      File file = SD.open("/channels.csv", FILE_APPEND);

      if (file) {

        file.write(upload.buf, upload.currentSize);

        file.close();

      }

    } else if (upload.status == UPLOAD_FILE_END) {

      // Validasi format file

      if (!validateCsvFormat()) {

        SD.remove("/channels.csv");

        server.send(400, "text/plain", "Invalid CSV format");

      }

    }

  });

  

  server.begin();

}


// Validasi format CSV

bool validateCsvFormat() {

  File file = SD.open("/channels.csv");

  if (!file) return false;

  

  bool valid = true;

  int lineCount = 0;

  

  while (file.available() && valid) {

    String line = file.readStringUntil('\n');

    line.trim();

    

    if (line.length() > 0) {

      int commaPos = line.indexOf(',');

      if (commaPos == -1) {

        valid = false;

        break;

      }

      

      String idStr = line.substring(0, commaPos);

      int id = idStr.toInt();

      if (id < 0 || id >= 64) {

        valid = false;

        break;

      }

      

      lineCount++;

    }

  }

  

  file.close();

  return valid && (lineCount == 64);

}


// Debug: Print konfigurasi sistem

void printSystemConfig() {

  Serial.println("System Configuration:");

  Serial.println("---------------------");

  Serial.println("Multiplexer Pins:");

  Serial.print("S0-S3: "); Serial.print(MUX_S0); Serial.print(", "); Serial.print(MUX_S1);

  Serial.print(", "); Serial.print(MUX_S2); Serial.print(", "); Serial.println(MUX_S3);

  Serial.print("SIG: "); Serial.println(MUX_SIG);

  Serial.print("SD CS: "); Serial.println(SD_CS);

  Serial.print("LCD Address: 0x"); Serial.println(LCD_ADDRESS, HEX);

  Serial.print("Active Threshold: "); Serial.println(ACTIVE_THRESHOLD);

  Serial.print("Max FIFO Entries: "); Serial.println(MAX_FIFO_ENTRIES);

  Serial.println("---------------------");

}


// Kirim data FIFO ke Serial Monitor

void sendFifoToSerial() {

  Serial.println("Current FIFO Contents:");

  Serial.println("ID\tName\t\tValue\tActive\tTimestamp");

  Serial.println("-----------------------------------------------");

  

  for (int i = 0; i < fifoCount; i++) {

    Serial.print(fifo[i].id); Serial.print("\t");

    Serial.print(fifo[i].name); 

    if (fifo[i].name.length() < 8) Serial.print("\t");

    Serial.print("\t");

    Serial.print(fifo[i].value); Serial.print("\t");

    Serial.print(fifo[i].active ? "Yes" : "No"); Serial.print("\t");

    Serial.println(fifo[i].timestamp);

  }

  

  Serial.println("-----------------------------------------------");

}

```


## Penjelasan Program:


1. **Konfigurasi Hardware**:

   - Menggunakan 4 pin kontrol (S0-S3) untuk multiplexer CD74HC4067

   - Pin GPIO36 (MUX_SIG) untuk membaca sinyal analog

   - SD card dihubungkan via SPI dengan CS pin GPIO5

   - LCD I2C di alamat 0x27 dengan ukuran 16x2


2. **FIFO (Antrian Data Channel Aktif)**:

   - Menyimpan hingga 10 entri (MAX_FIFO_ENTRIES)

   - Setiap entri berisi: ID, nama, nilai, status aktif, dan timestamp

   - Hanya menyimpan channel dengan nilai > ACTIVE_THRESHOLD (2000)

   - Menghapus entri saat channel tidak aktif lagi

   - Mengurutkan berdasarkan timestamp (fungsi sortFifoByTimestamp)


3. **Web Interface**:

   - Menampilkan tabel channel aktif di root path ("/")

   - Memungkinkan upload file CSV baru via form POST

   - Menyediakan link untuk download file channels.csv saat ini

   - Validasi format CSV saat upload (fungsi validateCsvFormat)


4. **Pembacaan MUX & Channel**:

   - Membaca semua 64 channel secara berurutan (fungsi readAllChannels)

   - Menggunakan threshold 2000 (ACTIVE_THRESHOLD) untuk deteksi aktif

   - Nama channel dimuat dari file channels.csv di SD card (fungsi loadChannelNames)


5. **LCD Display**:

   - Baris pertama menampilkan jumlah channel aktif

   - Baris kedua menampilkan jumlah data di FIFO

   - Diupdate setiap 1 detik (fungsi updateLcd)


6. **Fungsi Tambahan**:

   - `printSystemConfig()` untuk debugging konfigurasi sistem

   - `sendFifoToSerial()` untuk mengirim data FIFO ke serial monitor (dipicu dengan mengirim 'f' ke serial)

   - Validasi format CSV saat upload


## Instruksi Penggunaan:


1. Buat file channels.csv di SD card dengan format:

   ```

   0,Channel 1

   1,Channel 2

   ...

   63,Channel 64

   ```


2. Upload program ke ESP32


3. Hubungkan hardware sesuai konfigurasi pin


4. Akses antarmuka web melalui IP yang ditampilkan di LCD


5. Untuk debugging, buka Serial Monitor dan ketik 'f' untuk melihat isi FIFO


Program ini akan secara otomatis mendeteksi channel aktif, menyimpannya dalam FIFO, dan menampilkannya di LCD serta antarmuka web. 

Program ESP32 untuk Multiplexer 64 Channel dengan FIFO dan Web Interface

Program ESP32  untuk Multiplexer 64 Channel dengan FIFO dan Web Interface + LCD: ```cpp #include <Wire.h> #include <LiquidCrystal_I...