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:
Post a Comment