// From https://randomnerdtutorials.com/esp32-mqtt-publish-subscribe-arduino-ide

#include <WiFi.h> // include WiFi.h library
#include "PubSubClient.h" // include PubSubClient.h library

const char* ssid = "Wokwi-GUEST"; // mendefinisikan ssid sebagai "Wokwi-GUEST"
const char* password = ""; // mendefinisikan password sebagai ""
const char* mqttServer = "broker.emqx.io"; // mendefinisikan mqttServer sebagai "broker.emqx.io"
int port = 1883; //mendefinisikan port MQTT
String stMac; //menyimpan nilai menggunakan karakter string
char mac[50]; //menentukanpanjang karakter mac sebanyak 50 karakter
char clientId[50]; //menentukan panjang karakter clienId sepanjang 50 karakter

WiFiClient espClient;
PubSubClient client(espClient);

const int ledPin = 2; //mendefinisikan pin 2 sebagai ledPin

void setup() {
  Serial.begin(115200); //menjalankan serial monitor dengan abud rate 115200
  randomSeed(analogRead(0));

  delay(10);
  Serial.println();
  Serial.print("Connecting to "); //memunculkan "connecting to" pada serial monitor
  Serial.println(ssid); //memunculkan ssid pada serial monitor

  wifiConnect();

  Serial.println("");
  Serial.println("WiFi connected"); //memunculkan "WiFi connected pada serial monitor"
  Serial.println("IP address: "); //memunculkan "IP address :" pada serial monitor
  Serial.println(WiFi.localIP()); //menampilkan variable WiFi.localIP pada serial monitor
  Serial.println(WiFi.macAddress()); //menampilkan variable WiFi.macAddress() pada serial monitor
  stMac = WiFi.macAddress(); //menyimpan variable WiFi.macAddress() 
  stMac.replace(":", "_"); //mengganti karakter : mennjadi _
  Serial.println(stMac); //menampilkan variable stMac pada serial monitor
  
  client.setServer(mqttServer, port); //mesetting mqttServer dan port
  client.setCallback(callback); //melakukan callback pada peristiwa tertentu
  pinMode(ledPin, OUTPUT); //mengatur ledPin sebagai OUTPUT
}

void wifiConnect() {
  WiFi.mode(WIFI_STA); //mengatur WiFi mode menjadi STATION
  WiFi.begin(ssid, password); //memproses koneksi WiFi dengan ssid dan password
  while (WiFi.status() != WL_CONNECTED) { //jika WiFi.status() tidak sama dengan Wl_CONNECTED
    delay(500);
    Serial.print("."); //tampilkan "." pada serial monitor
  }
}

void mqttReconnect() {
  while (!client.connected()) { //jika client tidak terkonek
    Serial.print("Attempting MQTT connection..."); //menampilkan "Attempting MQTT connection" di serial monitor
    long r = random(1000); //menghasilkan bilangan bulat acak yg disimpan pada variabel r
    sprintf(clientId, "clientId-%ld", r); //melakukan format string dengan nilai variabel.
    if (client.connect(clientId)) { //jika client.connect terhubung dengan clientId
      Serial.print(clientId); //menampilkan clientId pada serial monitor
      Serial.println(" connected"); //menampilkan "connected" pada serial monitor
      client.subscribe("topicName/led"); //client mensubscribe pada "topicName/led"
    } else { //jika tidak
      Serial.print("failed, rc="); //menampilkan "failed, rc="
      Serial.print(client.state()); //menampilkan client.state pada serial monitor
      Serial.println(" try again in 5 seconds"); //menampilkan "try again in 5 seconds" pada serial monitor
      delay(5000);
    }
  }
}

void callback(char* topic, byte* message, unsigned int length) { //melakukan callback
  Serial.print("Message arrived on topic: "); //menampilkan "Message arrived on topic: " pada serial monitor
  Serial.print(topic); //menampilkan variabel topic pada serial monitor
  Serial.print(". Message: "); //menampilkan ". Message: " pada serial monitor
  String stMessage; //menyimpan data ke stMessage
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    stMessage += (char)message[i];
  }
  Serial.println();

  if (String(topic) == "topicName/led") { //jika String(topic) sama dengan "topicName/led"
    Serial.print("Changing output to "); //memunculkan "Changing output to"
    if(stMessage == "on"){ //jika stMessage sama dengan "on"
      Serial.println("on"); //memunculkan "on" pada serial monitor
      digitalWrite(ledPin, HIGH); //mengatur value ledPin menjadi HIGH
    }
    else if(stMessage == "off"){ //kalau tidak jika stMessage sama dengan "off"
      Serial.println("off"); //memunculkan "off" pada serial monitor
      digitalWrite(ledPin, LOW); //mengatur value ledPin menjadi LOW
    }
  }
}

void loop() {
  delay(10);
  if (!client.connected()) { //mengecek apakah client tidak terhubung
    mqttReconnect(); //lakukan mqttReconnect
  }
  client.loop(); //mempastikan mqtt tetap terkoneksi dan memproses pesan pesan yang masuk
}