Belajar Elektro

Random post

Belajar Elektro

Powered By Blogger

Wednesday, 17 September 2025

DTMF X IOT CONTROLER 4 CH

 











KODE PROGRAM DTMF X IOT 4 CH

ESP32 lolin Lite dan MT8070

KONTROL DTMF 4 CHANEL DENGAN ESP32 LOLIN LITE LAYAR 0.96 SPI WEB SERVER

3.3V  -------------------------- OLED VCC

GND   -------------------------- OLED GND

GPIO18 (SPI CLK) -------------- OLED D0 (SCK)

GPIO23 (SPI MOSI) ------------- OLED D1 (MOSI)

GPIO5  ------------------------ OLED RES

GPIO17 ------------------------ OLED DC

GPIO16 ------------------------ OLED CS

 

5V    -------------------------- MT8870 VCC

GND   -------------------------- MT8870 GND

GPIO32 ------------------------ MT8870 Q1

GPIO33 ------------------------ MT8870 Q2

GPIO25 ------------------------ MT8870 Q3

GPIO26 ------------------------ MT8870 Q4

GPIO27 ------------------------ MT8870 StQ

 

GPIO4  ------------------------ Relay 1 IN

GPIO13 ------------------------ Relay 2 IN

GPIO14 ------------------------ Relay 3 IN

GPIO12 ------------------------ Relay 4 IN

5V     ------------------------ Relay VCC (pastikan sesuai modul)

GND    ------------------------ Relay GND

 

GPIO19 ------------------------ Buzzer +

GND    ------------------------ Buzzer –

KODE PROGRAM :

#include <SPI.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

 

#include <WiFi.h>

#include <WebServer.h>

 

#include <Preferences.h>  // untuk menyimpan status relay

 

// ===== OLED SPI Pins =====

#define OLED_MOSI   23

#define OLED_CLK    18

#define OLED_RESET  5

#define OLED_DC     17

#define OLED_CS     16

 

#define SCREEN_WIDTH 128

#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC, OLED_RESET, OLED_CS);

 

// ===== DTMF MT8870 input =====

const int dtmfPins[4] = {32, 33, 25, 26};  // Q1..Q4

const int stqPin = 27;                    // data valid StQ

 

// ===== Relay pins =====

const int relays[4] = {4, 13, 14, 12};

bool relayState[4] = {false, false, false, false};

 

// ===== Buzzer =====

const int buzzerPin = 19;

 

// ===== WiFi Station =====

const char* ssid = "Tes123";      // Ganti dengan nama WiFi rumahmu

const char* password = "1234Dcba12";  // Ganti dengan password WiFi

 

// ===== Web server on port 80 =====

WebServer server(80);

 

// ===== Preferences (EEPROM emulasi) =====

Preferences preferences;

const char* prefNamespace = "relay_states";  // nama namespace

const char* prefKey = "states";             // kunci penyimpanan

 

int lastDTMF = -1;

 

// ===== Fungsi pembantu =====

char decodeDTMF(int value) {

  switch (value) {

    case 0x1: return '1';

    case 0x2: return '2';

    case 0x3: return '3';

    case 0x4: return '4';

    case 0x5: return '5';

    case 0x6: return '6';

    case 0x7: return '7';

    case 0x8: return '8';

    case 0x9: return '9';

    case 0xA: return '0';

    case 0xB: return '*';

    case 0xC: return '#';

    default: return '?';

  }

}

 

void buzz(int frequency, int duration) {

  tone(buzzerPin, frequency, duration);

  delay(duration);

  noTone(buzzerPin);

}

 

void playNokiaMelody() {

  int melody[] = {

    659, 659, 0, 659, 0, 523, 659, 784,

    0, 392, 0, 523, 0, 392, 0

  };

  int noteDurations[] = {

    150, 150, 100, 150, 100, 150, 150, 300,

    150, 300, 150, 150, 150, 150, 300

  };

 

  for (int i = 0; i < sizeof(melody)/sizeof(int); i++) {

    if (melody[i] == 0) {

      noTone(buzzerPin);

    } else {

      tone(buzzerPin, melody[i], noteDurations[i]);

    }

    delay(noteDurations[i] + 20);

  }

  noTone(buzzerPin);

}

 

void playRelayTone(int relayIndex, bool isOn) {

  // Nada berbeda tiap relay untuk ON dan OFF

  int freqOn[4] = {523, 587, 659, 698};   // C5, D5, E5, F5

  int freqOff[4] = {349, 330, 294, 262};  // F4, E4, D4, C4

  if (isOn) {

    buzz(freqOn[relayIndex], 150);

  } else {

    buzz(freqOff[relayIndex], 150);

  }

}

 

void displayCenteredText(const char* line1, const char* line2) {

  display.clearDisplay();

  display.setTextColor(SSD1306_WHITE);

 

  display.setTextSize(1);

  int16_t x1, y1;

  uint16_t w, h;

 

  display.getTextBounds(line1, 0, 0, &x1, &y1, &w, &h);

  int16_t xPos1 = (SCREEN_WIDTH - w) / 2;

 

  display.getTextBounds(line2, 0, 0, &x1, &y1, &w, &h);

  int16_t xPos2 = (SCREEN_WIDTH - w) / 2;

 

  int16_t yPos1 = (SCREEN_HEIGHT / 2) - 9;

  int16_t yPos2 = yPos1 + 10;

 

  display.setCursor(xPos1, yPos1);

  display.print(line1);

  display.setCursor(xPos2, yPos2);

  display.print(line2);

 

  display.display();

}

 

void displayStatus(char key) {

  display.clearDisplay();

  display.setCursor(0, 0);

  display.setTextSize(1);

  display.setTextColor(SSD1306_WHITE);

 

  display.print("Key: ");

  display.println(key);

  display.println("PILIHAN ONLINE:");

 

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

    display.print("[");

    display.print(i + 1);

    display.print("] ");

    display.println(relayState[i] ? "ON " : "OFF");

  }

 

  display.print("IP: ");

  display.println(WiFi.localIP());

 

  display.display();

}

 

void updateRelays() {

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

    digitalWrite(relays[i], relayState[i] ? HIGH : LOW);

  }

}

 

void saveRelayStates() {

  uint8_t b = 0;

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

    if (relayState[i]) {

      b |= (1 << i);

    }

  }

  preferences.putUInt(prefKey, b);

}

 

void loadRelayStates() {

  uint32_t b = preferences.getUInt(prefKey, 0);

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

    relayState[i] = (b & (1 << i)) != 0;

  }

}

 

// ===== Web UI handler =====

 

String generateWebPage() {

  String page = R"rawliteral(

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <title>ESP32 Relay Control</title>

  <style>

    body {

      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

      background: #121212;

      color: #eee;

      margin: 0;

      padding: 20px;

      text-align: center;

    }

    h2 {

      color: #00cc66;

    }

    .relay-container {

      display: flex;

      justify-content: center;

      flex-wrap: wrap;

      gap: 15px;

      margin-top: 20px;

    }

    .relay {

      background: #333;

      border-radius: 8px;

      box-shadow: 0 0 8px #00cc66;

      padding: 20px 30px;

      min-width: 120px;

      cursor: pointer;

      transition: background 0.3s ease, color 0.3s ease;

      user-select: none;

      font-weight: 600;

      font-size: 1.1em;

    }

    .relay:hover {

      background: #00cc66;

      color: #111;

    }

    .relay.on {

      background: #00cc66;

      color: #111;

      box-shadow: 0 0 12px #00cc66;

    }

    a {

      text-decoration: none;

      color: inherit;

      display: block;

      font-weight: bold;

    }

    footer {

      margin-top: 40px;

      font-size: 0.9em;

      color: #666;

    }

  </style>

</head>

<body>

  <h2>PILIHAN ONLINE</h2>

  <div class="relay-container">

)rawliteral";

 

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

    page += "<div class=\"relay ";

    page += (relayState[i] ? "on" : "");

    page += "\"><a href=\"/relay";

    page += String(i + 1);

    page += "_";

    page += (relayState[i] ? "off" : "on");

    page += "\">Relay ";

    page += String(i + 1);

    page += "<br>";

    page += (relayState[i] ? "ON" : "OFF");

    page += "</a></div>";

  }

 

  page += R"rawliteral(

  </div>

  <footer>IP: )rawliteral";

 

  page += WiFi.localIP().toString();

 

  page += R"rawliteral(

  </footer>

</body>

</html>

)rawliteral";

 

  return page;

}

 

void handleRoot() {

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

}

 

void handleRelay(int idx, bool turnOn) {

  relayState[idx] = turnOn;

  updateRelays();

  saveRelayStates();

  displayStatus('*'); // refresh OLED dengan tanda '*'

  playRelayTone(idx, turnOn);

  server.sendHeader("Location", "/");

  server.send(302, "text/plain", "");

}

 

void setupWebHandlers() {

  server.on("/", HTTP_GET, handleRoot);

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

    String onPath = "/relay" + String(i + 1) + "_on";

    String offPath = "/relay" + String(i + 1) + "_off";

    server.on(onPath.c_str(), HTTP_GET, [i]() {

      handleRelay(i, true);

    });

    server.on(offPath.c_str(), HTTP_GET, [i]() {

      handleRelay(i, false);

    });

  }

  server.begin();

}

 

void setup() {

  Serial.begin(115200);

 

  // OLED init

  SPI.begin(OLED_CLK, -1, OLED_MOSI, OLED_CS);

  if (!display.begin(SSD1306_SWITCHCAPVCC)) {

    Serial.println("OLED FAILED");

    while(true);

  }

 

  pinMode(buzzerPin, OUTPUT);

  digitalWrite(buzzerPin, LOW);

 

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

    pinMode(dtmfPins[i], INPUT);

    pinMode(relays[i], OUTPUT);

    digitalWrite(relays[i], LOW);

  }

  pinMode(stqPin, INPUT);

 

  preferences.begin(prefNamespace, false);

  loadRelayStates();

  updateRelays();

 

  // Opening tulisan center kecil dan rapi

  displayCenteredText("WELCOME TO", "PILIHAN ONLINE");

 

  // Nada pembuka Nokia style

  playNokiaMelody();

 

  delay(3000); // biar tulisan tetap tampil 3 detik setelah nada

 

  displayStatus(' '); // tampilkan status relay dan IP

 

  // WiFi connect

  WiFi.begin(ssid, password);

  Serial.print("Menghubungkan ke WiFi");

 

  int retry = 0;

  while (WiFi.status() != WL_CONNECTED && retry < 20) {

    delay(500);

    Serial.print(".");

    retry++;

  }

 

  if (WiFi.status() == WL_CONNECTED) {

    Serial.println();

    Serial.print("Terhubung ke WiFi! IP: ");

    Serial.println(WiFi.localIP());

  } else {

    Serial.println();

    Serial.println("Gagal terhubung ke WiFi.");

  }

 

  displayStatus(' ');

 

  setupWebHandlers();

}

 

void loop() {

  server.handleClient();

 

  if (digitalRead(stqPin) == HIGH) {

    int value = 0;

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

      value |= (digitalRead(dtmfPins[i]) << i);

    }

 

    if (value != lastDTMF) {

      lastDTMF = value;

      char key = decodeDTMF(value);

 

      if (key != '?') {

        Serial.print("Key: ");

        Serial.println(key);

 

        // Nada DTMF tekan tombol

        buzz(1000, 100);

 

        switch (key) {

          case '1': relayState[0] = true; playRelayTone(0, true); break;

          case '2': relayState[1] = true; playRelayTone(1, true); break;

          case '3': relayState[2] = true; playRelayTone(2, true); break;

          case '4': relayState[3] = true; playRelayTone(3, true); break;

          case '5': relayState[0] = relayState[1] = false; playRelayTone(0, false); playRelayTone(1, false); break;

          case '6': relayState[2] = relayState[3] = false; playRelayTone(2, false); playRelayTone(3, false); break;

          case '7': relayState[0] = !relayState[0]; playRelayTone(0, relayState[0]); break;

          case '8': relayState[1] = !relayState[1]; playRelayTone(1, relayState[1]); break;

          case '9': relayState[2] = !relayState[2]; playRelayTone(2, relayState[2]); break;

          case '0': relayState[3] = !relayState[3]; playRelayTone(3, relayState[3]); break;

        }

 

        updateRelays();

        saveRelayStates();

        displayStatus(key);

      }

    }

  } else {

    lastDTMF = -1; // reset jika tidak ada sinyal

  }

}

 

 















Kode fix dengan Auto Reconect

#include <SPI.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <WiFi.h>

#include <WebServer.h>

#include <Preferences.h>

 

// ===== OLED SPI Pins =====

#define OLED_MOSI   23

#define OLED_CLK    18

#define OLED_RESET  5

#define OLED_DC     17

#define OLED_CS     16

 

#define SCREEN_WIDTH 128

#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC, OLED_RESET, OLED_CS);

 

// ===== DTMF MT8870 input =====

const int dtmfPins[4] = {32, 33, 25, 26};  // Q1..Q4

const int stqPin = 27;                    // data valid StQ

 

// ===== Relay pins =====

const int relays[4] = {4, 13, 14, 12};

bool relayState[4] = {false, false, false, false};

 

// ===== Buzzer =====

const int buzzerPin = 19;

 

// ===== WiFi Station =====

const char* ssid = "Tes123";          // Ganti dengan WiFi kamu

const char* password = "1234Dcba12";  // Ganti dengan password WiFi

 

// ===== Web server on port 80 =====

WebServer server(80);

 

// ===== Preferences (EEPROM emulasi) =====

Preferences preferences;

const char* prefNamespace = "relay_states";

const char* prefKey = "states";

 

int lastDTMF = -1;

bool isApMode = false;

 

// ===== Fallback to AP Mode =====

void startAccessPoint() {

  const char* apSSID = "PILIHAN-ONLINE";

  const char* apPassword = "12345678";

  WiFi.softAP(apSSID, apPassword);

  isApMode = true;

 

  Serial.println("Fallback ke AP Mode.");

  Serial.print("AP IP: ");

  Serial.println(WiFi.softAPIP());

 

  displayCenteredText("AP MODE ACTIVE", WiFi.softAPIP().toString().c_str());

}

 

// ===== WiFi Auto Reconnect =====

void reconnectWiFi() {

  if (!isApMode && WiFi.status() != WL_CONNECTED) {

    Serial.println("WiFi terputus. Mencoba reconnect...");

    WiFi.disconnect();

    WiFi.begin(ssid, password);

 

    int retry = 0;

    while (WiFi.status() != WL_CONNECTED && retry < 20) {

      delay(500);

      Serial.print(".");

      retry++;

    }

 

    if (WiFi.status() == WL_CONNECTED) {

      Serial.println("\nBerhasil reconnect!");

      displayStatus(' ');

    } else {

      Serial.println("\nGagal reconnect. Mengaktifkan AP Mode.");

      startAccessPoint();

    }

  }

}

 

// ===== Utilitas DTMF =====

char decodeDTMF(int value) {

  switch (value) {

    case 0x1: return '1';

    case 0x2: return '2';

    case 0x3: return '3';

    case 0x4: return '4';

    case 0x5: return '5';

    case 0x6: return '6';

    case 0x7: return '7';

    case 0x8: return '8';

    case 0x9: return '9';

    case 0xA: return '0';

    case 0xB: return '*';

    case 0xC: return '#';

    default: return '?';

  }

}

 

void buzz(int frequency, int duration) {

  tone(buzzerPin, frequency, duration);

  delay(duration);

  noTone(buzzerPin);

}

 

void playNokiaMelody() {

  int melody[] = {

    659, 659, 0, 659, 0, 523, 659, 784,

    0, 392, 0, 523, 0, 392, 0

  };

  int noteDurations[] = {

    150, 150, 100, 150, 100, 150, 150, 300,

    150, 300, 150, 150, 150, 150, 300

  };

 

  for (int i = 0; i < sizeof(melody)/sizeof(int); i++) {

    if (melody[i] == 0) {

      noTone(buzzerPin);

    } else {

      tone(buzzerPin, melody[i], noteDurations[i]);

    }

    delay(noteDurations[i] + 20);

  }

  noTone(buzzerPin);

}

 

void playRelayTone(int relayIndex, bool isOn) {

  int freqOn[4] = {523, 587, 659, 698};

  int freqOff[4] = {349, 330, 294, 262};

  buzz(isOn ? freqOn[relayIndex] : freqOff[relayIndex], 150);

}

 

void displayCenteredText(const char* line1, const char* line2) {

  display.clearDisplay();

  display.setTextColor(SSD1306_WHITE);

  display.setTextSize(1);

  int16_t x1, y1;

  uint16_t w, h;

 

  display.getTextBounds(line1, 0, 0, &x1, &y1, &w, &h);

  display.setCursor((SCREEN_WIDTH - w) / 2, 20);

  display.println(line1);

 

  display.getTextBounds(line2, 0, 0, &x1, &y1, &w, &h);

  display.setCursor((SCREEN_WIDTH - w) / 2, 35);

  display.println(line2);

 

  display.display();

}

 

void displayStatus(char key) {

  display.clearDisplay();

  display.setCursor(0, 0);

  display.setTextSize(1);

  display.setTextColor(SSD1306_WHITE);

  display.print("Key: ");

  display.println(key);

  display.println("PILIHAN ONLINE:");

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

    display.print("[");

    display.print(i + 1);

    display.print("] ");

    display.println(relayState[i] ? "ON " : "OFF");

  }

  display.print("IP: ");

  display.println(isApMode ? WiFi.softAPIP() : WiFi.localIP());

  display.display();

}

 

void updateRelays() {

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

    digitalWrite(relays[i], relayState[i] ? HIGH : LOW);

  }

}

 

void saveRelayStates() {

  uint8_t b = 0;

  for (int i = 0; i < 4; i++) if (relayState[i]) b |= (1 << i);

  preferences.putUInt(prefKey, b);

}

 

void loadRelayStates() {

  uint32_t b = preferences.getUInt(prefKey, 0);

  for (int i = 0; i < 4; i++) relayState[i] = (b & (1 << i)) != 0;

}

 

// ===== Web UI =====

String generateWebPage() {

  String page = R"rawliteral(

<!DOCTYPE html><html><head>

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>ESP32 Relay Control</title>

<style>

body { background:#121212; color:#eee; font-family:sans-serif; text-align:center; padding:20px; }

h2 { color:#00cc66; }

.relay-container { display:flex; flex-wrap:wrap; justify-content:center; gap:15px; margin-top:20px; }

.relay { background:#333; padding:20px 30px; border-radius:8px; min-width:120px; cursor:pointer; transition:0.3s; font-weight:600; font-size:1.1em; box-shadow:0 0 8px #00cc66; }

.relay:hover { background:#00cc66; color:#111; }

.relay.on { background:#00cc66; color:#111; box-shadow:0 0 12px #00cc66; }

a { text-decoration:none; color:inherit; display:block; }

footer { margin-top:30px; color:#666; font-size:0.9em; }

</style></head><body><h2>PILIHAN ONLINE</h2><div class='relay-container'>

)rawliteral";

 

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

    page += "<div class='relay ";

    if (relayState[i]) page += "on";

    page += "'><a href='/relay";

    page += String(i + 1);

    page += relayState[i] ? "_off" : "_on";

    page += "'>Relay ";

    page += String(i + 1);

    page += "<br>";

    page += relayState[i] ? "ON" : "OFF";

    page += "</a></div>";

  }

 

  page += "</div><footer>IP: ";

  page += isApMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString();

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

  return page;

}

 

void handleRoot() {

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

}

 

void handleRelay(int idx, bool turnOn) {

  relayState[idx] = turnOn;

  updateRelays();

  saveRelayStates();

  displayStatus('*');

  playRelayTone(idx, turnOn);

  server.sendHeader("Location", "/");

  server.send(302, "text/plain", "");

}

 

void setupWebHandlers() {

  server.on("/", HTTP_GET, handleRoot);

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

    String onPath = "/relay" + String(i + 1) + "_on";

    String offPath = "/relay" + String(i + 1) + "_off";

    server.on(onPath.c_str(), HTTP_GET, [i]() { handleRelay(i, true); });

    server.on(offPath.c_str(), HTTP_GET, [i]() { handleRelay(i, false); });

  }

  server.begin();

}

 

// ===== SETUP & LOOP =====

void setup() {

  Serial.begin(115200);

  SPI.begin(OLED_CLK, -1, OLED_MOSI, OLED_CS);

  if (!display.begin(SSD1306_SWITCHCAPVCC)) while (true);

 

  pinMode(buzzerPin, OUTPUT);

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

    pinMode(dtmfPins[i], INPUT);

    pinMode(relays[i], OUTPUT);

    digitalWrite(relays[i], LOW);

  }

  pinMode(stqPin, INPUT);

 

  preferences.begin(prefNamespace, false);

  loadRelayStates();

  updateRelays();

 

  displayCenteredText("WELCOME TO", "PILIHAN ONLINE");

  playNokiaMelody();

  delay(3000);

  displayStatus(' ');

 

  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);

  WiFi.setAutoReconnect(true);

  WiFi.persistent(true);

 

  int retry = 0;

  while (WiFi.status() != WL_CONNECTED && retry < 20) {

    delay(500);

    retry++;

  }

 

  if (WiFi.status() == WL_CONNECTED) {

    isApMode = false;

  } else {

    startAccessPoint();

  }

 

  displayStatus(' ');

  setupWebHandlers();

}

 

void loop() {

  server.handleClient();

  reconnectWiFi();

 

  if (digitalRead(stqPin) == HIGH) {

    int value = 0;

    for (int i = 0; i < 4; i++) value |= (digitalRead(dtmfPins[i]) << i);

 

    if (value != lastDTMF) {

      lastDTMF = value;

      char key = decodeDTMF(value);

      if (key != '?') {

        buzz(1000, 100);

        switch (key) {

          case '1': relayState[0] = true; break;

          case '2': relayState[1] = true; break;

          case '3': relayState[2] = true; break;

          case '4': relayState[3] = true; break;

          case '5': relayState[0] = relayState[1] = false; break;

          case '6': relayState[2] = relayState[3] = false; break;

          case '7': relayState[0] = !relayState[0]; break;

          case '8': relayState[1] = !relayState[1]; break;

          case '9': relayState[2] = !relayState[2]; break;

          case '0': relayState[3] = !relayState[3]; break;

        }

        for (int i = 0; i < 4; i++) playRelayTone(i, relayState[i]);

        updateRelays();

        saveRelayStates();

        displayStatus(key);

      }

    }

  } else {

    lastDTMF = -1;

  }

}


0 comments: