// ESP32 connected with loadcell and HX711 amplifier to get the weight reading
// LCD to display the weight
// MQTT to receive the message

#include <WiFi.h>
#include <PubSubClient.h>
#include "HX711.h"
#include <Adafruit_SSD1306.h>
#include <LiquidCrystal_I2C.h>

const int LOADCELL_DOUT_PIN = 16;
const int LOADCELL_SCK_PIN = 4;

HX711 scale;
int reading;
int lastReading;
#define CALIBRATION_FACTOR   420 // Calibration factor need to be calculate, approximately -456

const char *ssid = "Wokwi-GUEST";
const char *password = "";

const char *mqtt_broker = "broker.emqx.io";
const char *topic = "ESP32-XLAYER";
const char *mqtt_username = "emqx";
const char *mqtt_password = "public";
const int mqtt_port = 1883;

int lcdColumns = 16;
int lcdRows = 2;

LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

WiFiClient espClient;
PubSubClient client(espClient);

void displayWeight(int weight) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Weight: ");
  lcd.print(weight);
  lcd.print(" Kg");
}

void setup() {
  Serial.begin(115200);

  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(CALIBRATION_FACTOR);
  scale.tare();

  lcd.init();
  lcd.backlight();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected");

  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);


while (!client.connected()) {
    String client_id = "esp32-client-";
    client_id += String(WiFi.macAddress());
    Serial.printf("The client %s connects to the public MQTT broker\n", client_id.c_str());

    if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
        Serial.println("MQTT broker connected");
    } else {
        Serial.print("failed with state ");
        Serial.print(client.state());
        delay(2000);
    }
}


  client.publish(topic, "The ESP32 is connected");
  client.subscribe(topic);
}

void callback(char *topic, byte *payload, unsigned int length) {
  Serial.print("Message arrived: ");
  Serial.println(topic);
  Serial.print("Message: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Display weight on LCD when message is received
  int receivedWeight = atoi((char *)payload);
  displayWeight(receivedWeight);    // display message
}

void loop() {
  client.loop();

  if (scale.wait_ready_timeout(200)) {
    reading = round(scale.get_units());
    Serial.print("Weight: ");
    Serial.println(reading);
    
    String weightMessage = "Weight: " + String(reading) + " Kg";
    client.publish(topic, weightMessage.c_str(), true);  // Setting the retain flag to true, to retain message that are stored in broker when the subscriber is offline
    // all message are not been retained only the last message is retained
    
    if (reading != lastReading) {
      displayWeight(reading);
    }
    lastReading = reading;
  } else {
    Serial.println("HX711 not found.");
  }

  delay(2000);
}