/*
  Optimized ADE9153A ESP32 Code with MQTT & WiFi
*/

#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

#include <SPI.h>
#include "ADE9153A.h"
#include "ADE9153AAPI.h"

#define SPI_SPEED 1000000     // SPI Speed
#define CS 15                 // Chip Select
#define STATE 2               // GPIO for state control
#define LED 33                // GPIO for LED

ADE9153AClass ade9153A;
struct PowerRegs powerVals;   
struct RMSRegs rmsVals;  

unsigned long lastReport = 0;
const long reportInterval = 2000;  // Every 2s

// WiFi Credentials
const char *ssid = "Wokwi-GUEST"; 
const char *password = "";  

// MQTT Broker
const char *mqtt_broker = "broker.mqttdashboard.com";
const char *topic = "Oversight_PM_In";
const char *publishTopic = "Oversight_PM_Out";
const int mqtt_port = 1883;

char buffer[512];  // Increased buffer size

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

WiFiClient espClient;
PubSubClient client(espClient);

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

  // Initialize SPI with explicit pin mapping
  SPI.begin(18, 19, 23, 15); // SCK=18, MISO=19, MOSI=23, CS=15
  
  // Initialize Pin Modes
  pinMode(CS, OUTPUT);
  pinMode(STATE, OUTPUT);
  pinMode(LED, OUTPUT);
  digitalWrite(STATE, LOW);

  // Initialize ADE9153A Chip
  if (!ade9153A.SPI_Init(SPI_SPEED, CS)) {
    Serial.println("ADE9153A Shield not detected. Restart ESP32.");
    while (true) { delay(1000); }
  }

  // Connect to WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to WiFi");

  // Set time offset for NTP
  timeClient.begin();
  timeClient.setTimeOffset(19800);

  // Connect to MQTT
  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);
  mqttReconnect();
  client.subscribe(topic);
}

void loop() {
  if (!client.connected()) {
    mqttReconnect();
  }
  client.loop();

  // Read Power values every 2s (Non-blocking)
  if (millis() - lastReport >= reportInterval) {
    lastReport = millis();
    readandwrite();
  }
}

void readandwrite() {
  ade9153A.ReadPowerRegs(&powerVals);
  ade9153A.ReadRMSRegs(&rmsVals);

  Serial.printf("RMS Current: %.3f A\n", rmsVals.CurrentRMSValue / 1000.0);
  Serial.printf("RMS Voltage: %.3f V\n", rmsVals.VoltageRMSValue / 1000.0);
  Serial.printf("Active Power: %.3f W\n", powerVals.ActivePowerValue / 1000.0);
  Serial.printf("Reactive Power: %.3f VAR\n", powerVals.FundReactivePowerValue / 1000.0);
  Serial.printf("Apparent Power: %.3f VA\n\n", powerVals.ApparentPowerValue / 1000.0);

  timeClient.update();
  String currentDate = String(timeClient.getEpochTime());

  DynamicJsonDocument doc(512);  // Increased buffer size
  doc["deviceId"] = "0001";
  doc["time_stamp"] = currentDate;
  doc["state"] = digitalRead(STATE);
  doc["energy"] = String(powerVals.ActivePowerValue / 1000.0, 2);
  doc["durationInSeconds"] = "1";
  doc["voltage"] = String(rmsVals.VoltageRMSValue / 1000.0, 2);
  doc["current"] = String(rmsVals.CurrentRMSValue / 1000.0, 2);
  doc["kva"] = String(powerVals.ApparentPowerValue / 1000.0, 2);
  doc["valid"] = 1;
  
  serializeJson(doc, buffer);
  client.publish(publishTopic, buffer, true);  // Retained message
}

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

  if ((char)payload[0] == '1') {
    digitalWrite(STATE, HIGH);
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(STATE, LOW);
    digitalWrite(LED, LOW);
  }
}

void mqttReconnect() {
  int retries = 5;
  while (!client.connected() && retries > 0) {
    String client_id = "esp32-client-";
    client_id += String(WiFi.macAddress());
    Serial.printf("Connecting to MQTT as %s...\n", client_id.c_str());

    if (client.connect(client_id.c_str())) {
      Serial.println("Connected to MQTT broker");
      client.subscribe(topic);
    } else {
      Serial.printf("Connection failed, retrying (%d left)...\n", retries);
      retries--;
      delay(2000);
    }
  }
  if (!client.connected()) {
    Serial.println("MQTT connection failed. Restarting ESP32...");
    ESP.restart();  // Restart if unable to connect
  }
}
ADE9153ABreakout