#include <Wire.h>
#include <Adafruit_ADS1X15.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <math.h>

// =================================
// Podłaczenie wyświetlacza z ESP32
// =================================
//GND -> GND     ESP32
//VCC -> 3,3V    ESP32
//SCL -> GPIO 18 ESP32
//SDA -> GPIO 22 ESP32
//RES -> GPIO 4  ESP32
//DC  -> GPIO 2  ESP32
//CS  -> GPIO 15 ESP32
//BLK -> 3,3V    ESP32

// ============================
// Podłaczenie ADS1115 Z ESP32
// ============================
//VCC -> 5V  ESP32
//GND -> GND ESP32

// ======================================================
// Podłaczenie ADS1115 Z Konwerterem poziomów logicznych
// ======================================================
//SDA -> 5V  HV1
//SCL -> GND HV2

// ===================================================
// Podłaczenie konwertera poziomów logicznych z ESP32 
// ===================================================
//HV  -> 5V      ESP32
//LV  -> 3,3V    ESP32
//GND -> GND     ESP32
//LV1 -> GPIO 21 ESP32
//LV2 -> GPIO 22 ESP32

// ==============================
// Podłaczenie przycisku z ESP32 
// ==============================
//dozwolona nóżka przycisku -> GPIO 16 ESP32
//nóżka po przekątnej       -> GND     ESP32

// ================================
// Podłaczenie mierzonego napięcia
// ================================
// plus (dodatni biegun) do pinu A0 ADS1115
// minus (masa) do pinu GND ADS1115

// ===============
// PINY I OBIEKTY
// ===============

#define BUTTON_PIN 16 

TFT_eSPI tft = TFT_eSPI();          // Ekran TFT
Adafruit_ADS1115 ads;               // Przetwornik ADS1115

// ==========================
// ZMIENNE GLOBALNE
// ==========================

float voltage = 0.0;                // Odczytane napięcie
unsigned long lastUpdate = 0;      // Czas ostatniego odświeżenia
bool clipping = false;             // Flaga clippingu (przekroczenia zakresu)

// Odświeżanie pomiaru (co 500 ms)
const int updateInterval = 500;

// Dostępne zakresy napięcia i odpowiadające ustawienia gain
const float pgaRanges[] = {0.256, 0.512, 1.024, 2.048, 4.096, 6.144};
adsGain_t gainMap[6] = {
  GAIN_SIXTEEN,
  GAIN_EIGHT,
  GAIN_FOUR,
  GAIN_TWO,
  GAIN_ONE,
  GAIN_TWOTHIRDS
};

int currentRangeIndex = 0;         // Aktualnie wybrany zakres

// Zmienne przycisku
bool lastButtonState = HIGH;
unsigned long lastDebounce = 0;

void drawFrame() {
  tft.fillScreen(TFT_BLACK);
  tft.fillRoundRect(0, 0, 240, 110, 42, TFT_DARKGREY);
  tft.drawRoundRect(0, 0, 240, 110, 42, TFT_WHITE);

  tft.setTextDatum(TL_DATUM);
  tft.setTextColor(TFT_CYAN, TFT_DARKGREY);
  tft.setTextFont(2);
  tft.drawString("V", 20, 10);
  tft.drawString("DC", 40, 10);
  tft.setTextColor(TFT_YELLOW, TFT_DARKGREY);
  tft.drawString("AUTO", 100, 10);
  tft.setTextColor(TFT_WHITE, TFT_DARKGREY);
  tft.drawString("HOLD", 180, 10);

  tft.fillRect(30, 50, 180, 55, TFT_DARKGREY);
  tft.setTextFont(7);
  tft.setTextDatum(MC_DATUM);
  tft.setTextColor(TFT_WHITE, TFT_DARKGREY);
  tft.drawString("0.0000", 120, 78);

  // Pasek
  tft.drawRect(30, 130, 180, 10, TFT_WHITE);
  tft.setTextFont(2);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(TL_DATUM);
  tft.drawString("0V", 30, 145);
  tft.setTextDatum(TR_DATUM);
  tft.drawString(String(pgaRanges[currentRangeIndex], 2) + "V", 210, 145);

  // Miejsce na clipping alert
  tft.fillRect(30, 170, 180, 20, TFT_BLACK);
}

void drawRangeInfo() 
{
  tft.setTextDatum(MC_DATUM);
  tft.setTextFont(2);
  tft.setTextColor(TFT_WHITE, TFT_DARKGREY);
  tft.fillRect(20, 30, 200, 15, TFT_DARKGREY);
  tft.drawString("Range: 0 - " + String(pgaRanges[currentRangeIndex], 3) + " V", 120, 38);
}

void updateVoltage() 
{
  int16_t raw = ads.readADC_SingleEnded(0);
  voltage = abs(ads.computeVolts(raw));

  // Wyświetlanie wartości napięcia
  tft.setTextDatum(MC_DATUM);
  tft.setTextFont(7);
  tft.setTextColor(TFT_WHITE, TFT_DARKGREY);
  tft.fillRect(30, 50, 180, 55, TFT_DARKGREY);
  tft.drawString(String(voltage, 4), 120, 78);

  // Pasek
  float range = pgaRanges[currentRangeIndex];
  int barWidth = map(voltage * 1000, 0, range * 1000, 0, 180);
  uint16_t barColor = TFT_GREEN;
  if (voltage > range * 0.7) barColor = TFT_ORANGE;
  if (voltage > range * 0.9) barColor = TFT_RED;

  tft.fillRect(31, 131, 178, 8, TFT_DARKGREY);
  tft.fillRect(31, 131, barWidth, 8, barColor);

  // Clipping z tolerancją ±8 (dla 16-bit)
  clipping = (raw >= 32760 || raw <= -32760);

  if (clipping) 
  {
    tft.setTextColor(TFT_RED, TFT_BLACK);
    tft.setTextFont(2);
    tft.setTextDatum(MC_DATUM);
    tft.drawString("Przekroczony zakres!", 120, 180);
  } else 
  {
    tft.fillRect(40, 175, 160, 16, TFT_BLACK);
  }
}

void checkButton() 
{
  bool buttonState = digitalRead(BUTTON_PIN);
  if (buttonState == LOW && lastButtonState == HIGH && millis() - lastDebounce > 300) 
  {
    currentRangeIndex = (currentRangeIndex + 1) % 6;
    ads.setGain(gainMap[currentRangeIndex]);
    drawFrame();
    drawRangeInfo();
    lastDebounce = millis();
  }
  lastButtonState = buttonState;
}

void setup() 
{
  tft.init();
  tft.setRotation(0);

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  if (!ads.begin()) 
  {
    while (1);  // ADS1115 nie wykryto
  }
  ads.setGain(gainMap[currentRangeIndex]);
  ads.setDataRate(RATE_ADS1115_8SPS);

  drawFrame();
  drawRangeInfo();
}

void loop() 
{
  if (millis() - lastUpdate > updateInterval) 
  {
    updateVoltage();
    lastUpdate = millis();
  }
  checkButton();
}
