#include <LiquidCrystal.h>
#include "AiEsp32RotaryEncoder.h"
#include "Arduino.h"
//#include <Wire.h>
//================= include wifi ==================
#include <WiFi.h>
#include <WebServer.h>
// Replace with your network credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// Create an instance of the WebServer on port 80
WebServer server(80);
//================= include wifi ==================
LiquidCrystal lcd(4, 16, 17, 5, 18, 19);
#define SW 21 // SW ENCODER
#define DT 22 // DT ENCODER
#define CLK 23 // CLK ENCODER
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(DT, CLK, SW, -1, 4);
#define Op1 0
#define Op2 1
#define Aux 2
#define Select 3
#define latchPIN 14
#define dataPIN 12
#define clockPIN 13
byte show[8][8]={
{0x07,0x0F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F},
{0x1F,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00},
{0x1C,0x1E,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F},
{0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1E,0x1C},
{0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F},
{0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x0F,0x07},
{0x1F,0x1F,0x1F,0x00,0x00,0x00,0x1F,0x1F},
{0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F}
};
const char pilTop[10][3]={0,1,2, 1,2,32, 6,6,2, 1,6,2, 5,4,7, 7,6,6, 0,6,6, 1,1,3, 0,6,2, 0,6,2};
const char pilBot[10][3]={5,4,3, 4,7,4, 7,4,4, 4,4,3, 32,32,7, 4,4,3, 5,4,3, 32,0,32, 5,4,3, 4,4,3};
unsigned char power,mulai,menu,inp,smd,mute,mut,hal,ton,trim,w;
uint8_t MVol,bass,treble,fL,fR,cn,sw,rL,rR;
int8_t fLm,fRm,cnm,swm,rLm,rRm;
boolean smdb=true, trimb=true, mutb=true;
int8_t tmp,sc;
unsigned long lasttime;
void tampil(unsigned int val, unsigned char x);
void setMUTE(uint8_t self);
void M62446AFP_Volume(uint8_t frontL,uint8_t frontR,uint8_t center,uint8_t subWoofer,uint8_t rearL,uint8_t rearR);
void M62446AFP_setVOLUME(uint8_t volume, uint8_t channel, uint8_t both);
void M62446AFP_setTONE(uint8_t tones, uint8_t channel, uint8_t bypass);
void writeDATA(uint16_t data);
//================= web support =================================================
void handleRoot() {
const char html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<title>ESP IOT DASHBOARD</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {
font-family: Arial, Helvetica,sans-serif;
display: inline-block; margin: 0px auto; text-align: center;
}
</style>
<script>
function btPower() { fetch('/power'); }
function btMute() { fetch('/mute'); }
function btBass() { fetch('/bass'); }
function btTreble() { fetch('/treble'); }
function btVolume() { fetch('/volume'); }
function btFrontL() { fetch('/frontl'); }
function btFrontR() { fetch('/frontr'); }
function btCenter() { fetch('/center'); }
function btSubwoofer() { fetch('/subwoofer'); }
function btRearL() { fetch('/rearl'); }
function btRearR() { fetch('/rearr'); }
function btUp() { fetch('/up'); }
function btDown() { fetch('/down'); }
function btInput() { fetch('/input'); }
function btMode() { fetch('/mode'); }
</script>
</head>
<body>
<h1>DIGIVOL 5.1</h1>
<div>
<p>
<button class="button" onclick="btPower()" style="width:100">POWER</button>
<button class="button" onclick="btUp()" style="width:100">UP</button>
<button class="button" onclick="btMute()" style="width:100">MUTE</button>
</p>
<p>
<button class="button" onclick="btBass()" style="width:100">Bass</button>
<button class="button" onclick="btDown()" style="width:100">DOWN</button>
<button class="button" onclick="btTreble()" style="width:100">Treble</button>
</p>
<p>
<button class="button" onclick="btFrontL()" style="width:100px">Front L</button>
<button class="button" onclick="btCenter()" style="width:100px">Center</button>
<button class="button" onclick="btFrontR()" style="width:100px">Front R</button>
</p>
<p>
<button class="button" onclick="btRearL()" style="width:100px">Rear L</button>
<button class="button" onclick="btSubwoofer()" style="width:100px" >Subwoofer</button>
<button class="button" onclick="btRearR()" style="width:100px">Rear R</button>
</p>
<p>
<button class="button" onclick="btInput()" style="width:50px">Input</button>
<button class="button" onclick="btVolume()" style="width:150px">VOLUME</button>
<button class="button" onclick="btMode()" style="width:50px">Mode</button>
</p>
</div>
</body>
</html>)rawliteral";
server.send(200, "text/html", html);
}
void handlePower(){
Serial.println("Power");
server.send(200);
}
void handleMute(){
Serial.println("Mute");
server.send(200);
}
void handleBass(){
Serial.println("Bass");
server.send(200);
}
void handleTreble(){
Serial.println("Treble");
server.send(200);
}
void handleVolume(){
Serial.println("Volume");
server.send(200);
}
void handleFrontL(){
Serial.println("Front Left");
server.send(200);
}
void handleFrontR(){
Serial.println("Front Right");
server.send(200);
}
void handleCenter(){
Serial.println("Center");
server.send(200);
}
void handleSubwoofer(){
Serial.println("Subwoofer");
server.send(200);
}
void handleRearL(){
Serial.println("Rear Left");
server.send(200);
}
void handleRearR(){
Serial.println("Rear Right");
server.send(200);
}
void handleUp(){
Serial.println("Up");
server.send(200);
}
void handleDown(){
Serial.println("Down");
server.send(200);
}
void handleInput(){
Serial.println("Input");
server.send(200);
}
void handleMode(){
Serial.println("Mode");
server.send(200);
}
void setForOption(int newOption){
menu = newOption;
switch (newOption)
{
case 0:
rotaryEncoder.setBoundaries(0, 79, false);
rotaryEncoder.setEncoderValue(MVol);
break;
case 2:
rotaryEncoder.setBoundaries(0, 1, false);
rotaryEncoder.setEncoderValue(smd);
break;
case 3:
switch(hal){
case 0:
rotaryEncoder.setBoundaries(0, 2, false);
rotaryEncoder.setEncoderValue(ton);
break;
case 1:
if(ton>=0 && ton<=1) rotaryEncoder.setBoundaries(0, 15, false);
switch(ton){
case 0: rotaryEncoder.setEncoderValue(bass); break;
case 1: rotaryEncoder.setEncoderValue(treble); break;
}
break;
}
break;
case 4:
switch(hal){
case 0:
rotaryEncoder.setBoundaries(0, 6, false);
rotaryEncoder.setEncoderValue(trim);
break;
case 1:
if(mute!=1){
if(trim>=0 && trim<=5) rotaryEncoder.setBoundaries(0, 79, false);
switch(trim){
case 0: rotaryEncoder.setEncoderValue(fL); break;
case 1: rotaryEncoder.setEncoderValue(fR); break;
case 2: rotaryEncoder.setEncoderValue(cn); break;
case 3: rotaryEncoder.setEncoderValue(sw); break;
case 4: rotaryEncoder.setEncoderValue(rL); break;
case 5: rotaryEncoder.setEncoderValue(rR); break;
}
}
break;
}
break;
case 5:
switch(hal){
case 0:
rotaryEncoder.setBoundaries(0, 1, false);
rotaryEncoder.setEncoderValue(mut);
break;
case 1:
if(mut==0){
rotaryEncoder.setBoundaries(0, 1, false);
rotaryEncoder.setEncoderValue(mute);
}
break;
}
break;
default: break;
}
}
void rotary_onButtonClick(){
static unsigned long lastTimePressed = 0;
//ignore multiple press in that time milliseconds
if (millis() - lastTimePressed < 500)
{
if(power==1){
menu++;
if(menu>6)menu=0;
}
return;
}
lastTimePressed = millis();
int selectedValue = rotaryEncoder.readEncoder();
switch(menu){
case 0: setForOption(1); break;
case 1: setForOption(2); break;
case 2: lcd.clear(); hal=0; ton=0; setForOption(3);break;
case 3:
if(hal==0){if(ton<2){hal=1; setForOption(3);}}
else
if(hal==1){if(ton<2){hal=0; setForOption(3);}}
if(ton==2){lcd.clear(); hal=0; trim=0; setForOption(4);}
break;
case 4:
if(hal==0){if(trim<6){hal=1; setForOption(4);}}
else
if(hal==1){if(trim<6){hal=0; setForOption(4);}}
if(trim==6){lcd.clear(); hal=0; setForOption(5);}
break;
case 5:
if(hal==0){if(mut==0){hal=1; setForOption(5);}}
else
if(hal==1){if(mut==0){hal=0; setForOption(5);}}
if(mut==1){lcd.clear(); hal=0; setForOption(0);}
break;
default: break;
}
if(power==0){
power=1; mulai=0;
// digitalWrite(Relay,HIGH);
}
}
void rotary_loop(){
if (rotaryEncoder.encoderChanged())
{
int selectedValue = rotaryEncoder.readEncoder();
switch(menu){
case 0:
tmp=MVol;
if(tmp<selectedValue) sc=1;
if(tmp>selectedValue) sc=-1;
if(mute!=1){
MVol=selectedValue;
if(fL!=MVol){fLm=(fL-MVol)+sc; fL=constrain(MVol+fLm,0,79);}
if(fR!=MVol){fRm=(fR-MVol)+sc; fR=constrain(MVol+fRm,0,79);}
if(cn!=MVol){cnm=(cn-MVol)+sc; cn=constrain(MVol+cnm,0,79);}
if(sw!=MVol){swm=(sw-MVol)+sc; sw=constrain(MVol+swm,0,79);}
if(rL!=MVol){rLm=(rL-MVol)+sc; rL=constrain(MVol+rLm,0,79);}
if(rR!=MVol){rRm=(rR-MVol)+sc; rR=constrain(MVol+rRm,0,79);}
}
break;
case 2:
smd=selectedValue;
if(smdb==true){fLm=fL; fRm=fR; cnm=cn; swm=sw; rLm=rL; rRm=rR;}
if(smd==0){smdb=false; cn=rL=rR=0;} else {smdb=true; cn=cnm; rL=rLm; rR=rRm;}
break;
case 3:
switch(hal){
case 0: ton=selectedValue; break;
case 1:
switch(ton){
case 0: bass=selectedValue; break;
case 1: treble=selectedValue; break;
}
break;
}
break;
case 4:
switch(hal){
case 0:
trim=selectedValue;
if(smd==0){
if(trim==2){
if(trimb==true){trimb=false; trim=3;}
else {trimb=true; trim=1;}
}
if(trim==4) trim=6;
if(trim==5) trim=3;
setForOption(4);
}
break;
case 1:
if(mute!=1){
switch(trim){
case 0: fL=selectedValue; break;
case 1: fR=selectedValue; break;
case 2: cn=selectedValue; break;
case 3: sw=selectedValue; break;
case 4: rL=selectedValue; break;
case 5: rR=selectedValue; break;
}
}
break;
}
break;
case 5:
switch(hal){
case 0: mut=selectedValue; break;
case 1:
if(mut==0){
mute=selectedValue;
if(mutb==true){fLm=fL; fRm=fR; cnm=cn; swm=sw; rLm=rL; rRm=rR;}
if(mute==1){mutb=false; fL=fR=cn=sw=rL=rR=0;} else {mutb=true; fL=fLm; fR=fRm; cn=cnm; sw=swm; rL=rLm; rR=rRm;}
}
break;
default: break;
}
break;
}
lasttime=millis(); w=1;
}
if (rotaryEncoder.isEncoderButtonClicked()){
rotary_onButtonClick();
lasttime=millis(); w=1;
}
}
void showSelection(){
if(power==1){
/* if(menu==0 && hal==0||menu>=3 && menu<=4 && hal==1){
for(byte i=0; i<8; i++) lcd.createChar(i, show[i]);
}
*/
if(mulai==0){
lcd.clear();
setMUTE(1);
lcd.setCursor(3,0);
lcd.print(F("A2T TECHNO"));
lcd.setCursor(0,1);
lcd.print(F("5.1 Audio System"));
delay(1000);
lcd.clear();
mute=0;
setMUTE(mute);
setForOption(0); //menu
}
//int selectedValue = rotaryEncoder.readEncoder();
switch(menu){
case 0:
//Input
if(digitalRead(Op1)==LOW && digitalRead(Op2)==HIGH && digitalRead(Aux)==HIGH) inp=0;
else if(digitalRead(Op1)==HIGH && digitalRead(Op2)==LOW && digitalRead(Aux)==HIGH) inp=1;
else if(digitalRead(Op1)==HIGH && digitalRead(Op2)==HIGH && digitalRead(Aux)==LOW) inp=2;
lcd.setCursor(0,0);
switch(inp){
case 0: lcd.print(F("OP1")); break;
case 1: lcd.print(F("OP2")); break;
case 2: lcd.print(F("AUX")); break;
}
//speaker mode
lcd.setCursor(13,0);
switch(smd){
case 0: lcd.print(F("2.1")); break;
case 1: lcd.print(F("5.1")); break;
}
lcd.setCursor(0,1);
if(mulai==0){
lcd.print(F("VOL>"));
for(unsigned char i=0; i<=MVol; i++){
tampil(i,5);
M62446AFP_Volume(i,i,i,i,i,i);
fL=fR=cn=sw=rL=rR=i;
delay(100);
}
mulai=1;
}
lcd.setCursor(0,1);
if(mute==0){lcd.print(F("VOL>")); tampil(MVol,5);}
if(mute==1){lcd.print(F("MUT<")); tampil(0,5);}
M62446AFP_Volume(fL,fR,cn,sw,rL,rR);
lcd.setCursor(3,0); lcd.print(F(" "));
lcd.setCursor(12,0); lcd.print(F(" "));
lcd.setCursor(12,1); lcd.print(F(" "));
break;
case 1:
/* if(counter==1){
digitalWrite(Select,HIGH); delay(200);
digitalWrite(Select,LOW); delay(200);
}
*/
if(digitalRead(Op1)==LOW && digitalRead(Op2)==HIGH && digitalRead(Aux)==HIGH) inp=0;
else if(digitalRead(Op1)==HIGH && digitalRead(Op2)==LOW && digitalRead(Aux)==HIGH) inp=1;
else if(digitalRead(Op1)==HIGH && digitalRead(Op2)==HIGH && digitalRead(Aux)==LOW) inp=2;
lcd.setCursor(0,0);
switch(inp){
case 0: lcd.print(F("OP1")); break;
case 1: lcd.print(F("OP2")); break;
case 2: lcd.print(F("AUX")); break;
}
lcd.setCursor(3,0); lcd.print(F("<"));
lcd.setCursor(3,1); lcd.print(F(" "));
lcd.setCursor(12,0); lcd.print(F(" "));
lcd.setCursor(12,1); lcd.print(F(" "));
break;
case 2:
lcd.setCursor(13,0);
switch(smd){
case 0: lcd.print(F("2.1")); break;
case 1: lcd.print(F("5.1")); break;
}
lcd.setCursor(3,0); lcd.print(F(" "));
lcd.setCursor(3,1); lcd.print(F(" "));
lcd.setCursor(12,0); lcd.print(F(">"));
lcd.setCursor(12,1); lcd.print(F(" "));
M62446AFP_Volume(fL,fR,cn,sw,rL,rR);
break;
case 3:
lcd.setCursor(0,0); lcd.print(F("T-CONTROL"));
lcd.setCursor(7,1);
if(ton!=2){
switch(hal){
case 0: lcd.print(F("<")); break;
case 1: lcd.print(F(">")); break;
}
}
lcd.setCursor(0,1);
switch(ton){
case 0: lcd.print(F("BASS ")); tampil(bass,10); break;
case 1: lcd.print(F("TREBLE")); tampil(treble,10); break;
case 2:
lcd.print(F("NEXT "));
lcd.setCursor(10,0);
lcd.print(F(" "));
break;
}
M62446AFP_setTONE(treble,0,0);
M62446AFP_setTONE(bass,1,0);
break;
case 4:
lcd.setCursor(0,0); lcd.print(F("VOL-TRIM"));
lcd.setCursor(8,1);
if(trim!=6){
switch(hal){
case 0: lcd.print(F("<")); break;
case 1: lcd.print(F(">")); break;
}
}
lcd.setCursor(0,1);
switch(trim){
case 0: lcd.print(F("FRONT L")); tampil(fL,10); break;
case 1: lcd.print(F("FRONT R")); tampil(fR,10); break;
case 2: lcd.print(F("CENTER ")); tampil(cn,10); break;
case 3: lcd.print(F("SWOOFER")); tampil(sw,10); break;
case 4: lcd.print(F("REAR L ")); tampil(rL,10); break;
case 5: lcd.print(F("REAR R ")); tampil(rR,10); break;
case 6:
lcd.print(F("NEXT "));
lcd.setCursor(10,0);
lcd.print(F(" "));
break;
}
M62446AFP_Volume(fL,fR,cn,sw,rL,rR);
break;
case 5:
lcd.setCursor(0,0); lcd.print(F("VOL-MUTE"));
lcd.setCursor(7,1);
if(mut!=1){
switch(hal){
case 0: lcd.print(F("<")); break;
case 1: lcd.print(F(">")); break;
}
}
lcd.setCursor(0,1);
switch(mut){
case 0:
lcd.print(F("MUTE"));
switch(mute){
case 0: lcd.setCursor(10,1); lcd.print(F("OFF ")); break;
case 1: lcd.setCursor(10,1); lcd.print(F("ON ")); break;
}
case 1:
lcd.print(F("NEXT "));
lcd.setCursor(10,0);
lcd.print(F(" "));
break;
}
setMUTE(mute);
break;
}
if(millis()-lasttime>10000 && mute==0 && w==1){
w=0; menu=0; hal=0; delay(300); lcd.clear();
setForOption(menu); //menu
}
}
else
{
mute=1;
setMUTE(mute);
delay(100);
lcd.setCursor(4,1);
lcd.print(F("STAND-BY"));
}
}
void IRAM_ATTR readEncoderISR(){
rotaryEncoder.readEncoder_ISR();
}
void setup() {
Serial.begin(115200);
//Wire.begin();
lcd.begin(16, 2);
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.disableAcceleration();
pinMode(Op1, INPUT);
pinMode(Op2, INPUT);
pinMode(Aux, INPUT);
pinMode(Select, OUTPUT);
//======================support wifi
// Connect to Wi-Fi
lcd.setCursor(0,0); lcd.print(F("Connecting to "));
lcd.setCursor(0,1); lcd.print(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("WiFi connected.");
//lcd.print("IP address: ");
lcd.setCursor(0,1);
lcd.print(WiFi.localIP());
delay(3000);
lcd.clear();
// Define routes
server.on("/", handleRoot);
server.on("/power", handlePower);
server.on("/mute", handleMute);
server.on("/bass", handleBass);
server.on("/treble", handleTreble);
server.on("/volume", handleVolume);
server.on("/frontl", handleFrontL);
server.on("/frontr", handleFrontR);
server.on("/center", handleCenter);
server.on("/subwoofer", handleSubwoofer);
server.on("/rearl", handleRearL);
server.on("/rearr", handleRearR);
server.on("/up", handleUp);
server.on("/down", handleDown);
server.on("/input", handleInput);
server.on("/mode", handleMode);
// Start the server
server.begin();
//================================
power=0;
MVol=50;
bass=treble=7;
smd=1;
for(byte i=0; i<8; i++) lcd.createChar(i, show[i]);
}
void loop() {
//--------------------------
server.handleClient();
//--------------------------
rotary_loop();
delay(50);
showSelection();
}
void tampil(unsigned int val, unsigned char x){
unsigned char a,b;
a=val / 10; b=val % 10;
lcd.setCursor(x, 0); for(uint8_t i=0; i < 3; i++) lcd.print(pilTop[a][i]);
lcd.setCursor(x, 1); for(uint8_t i=0; i < 3; i++) lcd.print(pilBot[a][i]);
lcd.setCursor(x+3, 0); for(uint8_t i=0; i < 3; i++) lcd.print(pilTop[b][i]);
lcd.setCursor(x+3, 1); for(uint8_t i=0; i < 3; i++) lcd.print(pilBot[b][i]);
}
void setMUTE(uint8_t self){
uint8_t a,b,c,d,e,f;
switch (self){
case 0: // mute off
a=fL; b=fR; c=cn; d=sw; e=rL; f=rR;
break;
case 1: // mute on
a=0; b=0; c=0; d=0; e=0; f=0;
break;
}
M62446AFP_Volume(a,b,c,d,e,f);
}
void M62446AFP_Volume(uint8_t frontL,uint8_t frontR,uint8_t center,uint8_t subWoofer,uint8_t rearL,uint8_t rearR){
M62446AFP_setVOLUME(frontL,0,1);
M62446AFP_setVOLUME(frontR,1,1);
M62446AFP_setVOLUME(center,0,2);
M62446AFP_setVOLUME(subWoofer,1,2);
M62446AFP_setVOLUME(rearL,0,3);
M62446AFP_setVOLUME(rearR,1,3);
}
void M62446AFP_setVOLUME(uint8_t volume, uint8_t channel, uint8_t both){
uint16_t data = 0;
if (both==0) return;
// batas volume 0 sampai 79
volume = volume < 0 ? 0 : volume > 79 ? 79 : volume;
data |= channel ? ((volume) & 0b0000000 << 0) : (0b0000000 & (volume) << 0); // D0 (select channel 0=FL,CN,RL 1=FR,SW,RR
switch (both){
case 1: //channel front L / R
data |= (0b01 << 14); // D14..D15
break;
case 2: //channel center / subwoofer
data |= (0b10 << 14); // D14..D15
break;
case 3: //channel rear L / R
data |= (0b11 << 14); // D14..D15
break;
}
writeDATA(data);
}
void M62446AFP_setTONE(uint8_t tones, uint8_t channel, uint8_t bypass){
uint16_t data = 0;
tones = tones < 0 ? 0 : tones > 13 ? 13 : tones;
if (tones < 5) tones = 13 - tones;
if (tones > 4) tones = tones - 5;
switch (channel){
case 0: //treble
data |= ((tones) << 0);
data |= (0b1111 << 4);
data |= (0b0000 << 8);
break;
case 1: //bass
data |= (0b0000 << 0);
data |= (0b1111 << 4);
data |= ((tones) << 8);
break;
}
if (bypass==0) data |= (0b0000 << 12); //tone aktif
else data |= (0b0100 << 12); //tone bypass
writeDATA(data);
}
void writeDATA(uint16_t data){
uint8_t bits;
delayMicroseconds(1);
digitalWrite(latchPIN, HIGH);
delayMicroseconds(3);
digitalWrite(latchPIN, LOW);
for (bits = 0; bits < 16; bits++){
delayMicroseconds(3);
digitalWrite(dataPIN, (data >> bits) & 0x01);
delayMicroseconds(3);
digitalWrite(clockPIN, 1);
delayMicroseconds(3);
digitalWrite(dataPIN, 0);
delayMicroseconds(3);
digitalWrite(clockPIN, 0);
}
delayMicroseconds(1);
digitalWrite(latchPIN, HIGH);
delayMicroseconds(3);
digitalWrite(latchPIN, LOW);
}