#include <Wire.h>
#include <RTClib.h>

#include <ModbusRtu.h>
#include <SoftwareSerial.h>

#include <SD.h>
#include <SPI.h>

#define DE_RE_PIN 4
#define RX_PIN 6     // SoftwareSerial RX pin
#define TX_PIN 8     // SoftwareSerial TX pin
#define SD_CS_PIN 10 // Chip Select for SD Card

RTC_DS3231 rtc; // Create an RTC object
File dataFile;

SoftwareSerial modbusSerial(RX_PIN, TX_PIN); // Create a software serial instance
Modbus slave(1, modbusSerial, DE_RE_PIN);
uint16_t holdingRegisters[20]; // Array to hold Modbus registers

void setup()
{
    Serial.begin(9600); // For debugging
    Serial.println("Startup \n");

    // Initialize RTC
    if (!rtc.begin())
    {
        Serial.println("Couldn't find RTC\n");
    }

    if (rtc.lostPower())
    {
        Serial.println("RTC lost power, let's set the time!\n");
        // Comment out the following line once the time is set to avoid resetting on every start
        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
    Serial.print("Time: ");
    Serial.print(rtc.now().timestamp());
    Serial.println("\n");

    // Initialize SD card
    Serial.println("SD card initializing...");
    pinMode(SD_CS_PIN, OUTPUT);
    if (!SD.begin(SPI_HALF_SPEED, SD_CS_PIN))
    {
        Serial.println("SD card initialization failed!\n");
        return;
    }
    Serial.println("SD card initialized.\n");

    Serial.println("Initialize RS485 module / Modbus \n");
    // Initialize RS485 module
    // Initialize DE and RE pins
    pinMode(DE_RE_PIN, OUTPUT);
    digitalWrite(DE_RE_PIN, LOW); // Set to LOW for receiving mode initially

    slave.start();

    // Initialize the holding registers
    // holdingRegisters[0] = 0x0000;
    // holdingRegisters[1] = 0x0000;
}

void writeFile(char *str)
{
    if (!dataFile)
    {
        Serial.println("Error opening file");
        return;
    }
    DateTime now = rtc.now();
    // Log the current date and time
    dataFile.print(now.year(), DEC);
    dataFile.print('-');
    dataFile.print(now.month(), DEC);
    dataFile.print('-');
    dataFile.print(now.day(), DEC);
    dataFile.print(" ");
    dataFile.print(now.hour(), DEC);
    dataFile.print(':');
    dataFile.print(now.minute(), DEC);
    dataFile.print(':');
    dataFile.print(now.second(), DEC);
    dataFile.print(",");
    dataFile.print(str);

    dataFile.println();
}

String getFilename()
{
    DateTime now = rtc.now();
    String mb = "";
    mb += now.year();
    mb += now.month();
    mb += now.day();
    mb += ".csv";

    return mb;
}

void loop()
{
    Serial.print("Time: ");
    Serial.print(rtc.now().timestamp());
    //   Serial.print("\nHeep:");
    //  Serial.print(ESP.getFreeHeap());
    Serial.print("\n");

    // Open file for writing
    String filename = getFilename();
    Serial.print("Open Card ");
    Serial.print(filename.c_str());
    Serial.print("\n");

    dataFile = SD.open(filename.c_str(), FILE_WRITE);

    Serial.print("Error count ");
    Serial.print(slave.getErrCnt());
    Serial.print("\n");
    Serial.print(slave.getLastError());
    Serial.print("\n");

    //
    const int result = slave.poll(holdingRegisters, 20);
    String mb = "modbus,";
    mb += result;
    mb += ",";
    for (int i = 0; i < 20; i++)
    {
        mb += holdingRegisters[i];
        mb += ",";
    }

    Serial.println(mb.c_str());
    writeFile(mb.c_str());

    if (modbusSerial.available() > 0)
    {
        Serial.println(modbusSerial.read());
    }

    if (dataFile)
    {
        dataFile.close(); // Close the file
        Serial.print("Data written to SD card: ");
        Serial.print(filename.c_str());
        Serial.print("\n");
    }

    Serial.print("\n\n");

    delay(1000); // Delay between readings
}
