#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <math.h>
//#include <OneWire.h>
//#include <DallasTemperature.h>
enum Status { NO = 0, YES = 1 };
byte zero[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};
byte one[] = {
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000
};
byte two[] = {
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000
};
byte three[] = {
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100
};
byte four[] = {
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110
};
byte five[] = {
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
struct Calibration {
  float voltmeter_full_scale;
  float ampmeter_full_scale;
  float power_drv_min;
  byte power_fwd_min;
  byte power_ref_min;
  byte power_drv_max;
  byte TXdelay;
  byte RXdelay;
  float temp_alarm_threshold;
  byte swr_threshold;
  float fan_temp_min;
  float fan_temp_max;
  byte fan_pwm_min;
  byte fan_pwm_max;
  float power_max;
  float Bvalue;
  float Rref;
  float a_pwr; 
  float b_pwr; 
  float c_pwr;
};
Calibration calib;
const int calibEEPROMAddr = 0; // startadres in EEPROM
constexpr byte FAN_PWM     = 9; // PWM-uitgang voor ventilator
// constexpr byte TEMP_SENSOR = 7; // DS18B20
constexpr byte SWR_ResetButton = 7;
constexpr byte PApwr       = 6;
constexpr byte AntRelay    = 4;
constexpr byte ALARM_PIN   = 3;
constexpr byte PTT         = 2;
constexpr byte FWD         = A0;
constexpr byte REV         = A1;
constexpr byte VOLT        = A2;
constexpr byte DRV         = A3;
constexpr byte AMP         = A6;
constexpr byte NTC         = A7;
const float a_drv = 0, b_drv = 0.227, c_drv = 2; //https://tools.softinery.com/CurveFitter/
struct Coupler {
  float voltage = 0;
  float power = 0;
  Status error = NO;
};
Coupler fwd, ref, drv;
float swr = 0, reflection_coefficient = 0;
float PSUvoltage = 0, PSUcurrent = 0;
float tempC;
bool AlarmState = false, TxState = false, TxEnable = true; 
bool PTTstate = false;
bool debugMode = false;
char buffer[8];
int pwmValue;
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
  pinMode(AntRelay, OUTPUT);
  pinMode(PApwr, OUTPUT);
  pinMode(ALARM_PIN, OUTPUT);
  pinMode(PTT, INPUT_PULLUP);
  pinMode(SWR_ResetButton, INPUT_PULLUP);
  digitalWrite(PTT, HIGH);
  digitalWrite(ALARM_PIN, LOW);
  pinMode(FAN_PWM, OUTPUT);
  // TCCR0B = TCCR0B & B11111000 | B00000010; // Prescaler 8 ? ~7.8 kHz
  // Stel Timer1 in op Fast PWM met 20 kHz
  TCCR1A = _BV(COM1A1) | _BV(WGM11);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // Geen prescaler
  ICR1 = 799; // 16 MHz / (799 + 1) = 20 kHz
  
  Serial.begin(115200);
  
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print(F("PA M O N I T O R"));
  lcd.setCursor(0, 1);
  lcd.print(F("21-09-2025 by PA3EKM"));
  delay(1000);
  lcd.clear();
  lcd.createChar(0, zero);
  lcd.createChar(1, one);
  lcd.createChar(2, two);
  lcd.createChar(3, three);
  lcd.createChar(4, four);
  lcd.createChar(5, five);
  EEPROM.get(calibEEPROMAddr, calib);
  // Als EEPROM leeg is (bijv. eerste keer), zet standaardwaarden
  if (isnan(calib.voltmeter_full_scale) || calib.voltmeter_full_scale == 0) {
    calib.voltmeter_full_scale = 66.0;
    calib.ampmeter_full_scale = 61.0;
    calib.power_drv_min = 0.1;
    calib.power_fwd_min = 1;
    calib.power_ref_min = 1;
    calib.power_drv_max = 8;
    calib.TXdelay = 50;
    calib.RXdelay = 50;
    calib.temp_alarm_threshold = 50.0;
    calib.swr_threshold = 3;
    calib.fan_temp_min = 20;
    calib.fan_temp_max = 50;
    calib.fan_pwm_min = 50;
    calib.fan_pwm_max = 255;
    calib.power_max = 400;
    calib.Bvalue = 3950;
    calib.Rref = 10000;
    calib.a_pwr = 0.0524; 
    calib.b_pwr = 10; 
    calib.c_pwr = 18;
    EEPROM.put(calibEEPROMAddr, calib);
  }
  showCalibration() ;
}
void loop() {
  PTTstate = digitalRead(PTT);
  if (!PTTstate && TxEnable && !TxState) {
    digitalWrite(AntRelay, HIGH);
    delay(calib.TXdelay);
    digitalWrite(PApwr, HIGH);
    Serial.println(F("TX on"));
    TxState = true;
  }
  if (PTTstate && TxState) {
    digitalWrite(PApwr, LOW);
    delay(calib.RXdelay);
    digitalWrite(AntRelay, LOW);
    Serial.println(F("TX off"));
    TxState = false;
  }
  readCouplers();
  if (AlarmState) alarm();
  displayCouplers();
  // measureTemp();
  readTemperature(NTC);
  if (AlarmState) alarm();
  measureSupply();
  displaySupply(); //and temp
  controlFan();
  if (debugMode)   Debugging();
  
  CLIinterface();
}
void CLIinterface() {
  if (Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();
    if (command.equalsIgnoreCase("HELP")) {
      showHelp();
    }
    else if (command.equalsIgnoreCase("DEBUG ON")) {
      debugMode = true;
      Serial.println(F("Debugmodus AAN"));
      Serial.println("PSU Current,Temperatuur,FWD power,REF power,DRV power,FWD voltage,FWD error,SWR,Fan-pwm,PSU Voltage");
    }
    else if (command.equalsIgnoreCase("DEBUG OFF")) {
      debugMode = false;
      Serial.println(F("Debugmodus UIT"));
    }
    else if (command.equalsIgnoreCase("SHOWCAL")) {
      showCalibration();
    } 
    else if (command.equalsIgnoreCase("RESETCAL")) {
      resetCalibrationEEPROM();  
    } 
    else if (command.startsWith("SET ")) {
      setCalibration(command);
    }
  }
}
   
char* dtos(float val, int width, int prec) {
  dtostrf(val, width, prec, buffer);
  return buffer;
}
void measureSupply() {
  PSUvoltage = analogRead(VOLT) * calib.voltmeter_full_scale / 1023;
  PSUcurrent = analogRead(AMP) * calib.ampmeter_full_scale / 1023;
}
/*void measureTemp() {  // DS18B20
  sensors.requestTemperatures();
  tempC = sensors.getTempCByIndex(0);
  AlarmState = (tempC >= calib.temp_alarm_threshold) ;
}*/
float readTemperature(int analogPin) {
//  [5V] --- [10k NTC] ---+--- [10k weerstand] --- [GND]
//                        |
//                     [A7 pin]
//  3950 is een typische B-waarde, maar check de datasheet van jouw NTC.
//  10000.0 is de weerstand bij 25°C.
  int raw = analogRead(analogPin);
  float resistance = (1023.0 / raw - 1.0) * calib.Rref; 
  tempC = 1.0 / (log(resistance / calib.Rref) / calib.Bvalue + 1.0 / 298.15) - 273.15;
  AlarmState = (tempC >= calib.temp_alarm_threshold) ;
}
void controlFan() {
  int pwmRaw = map(constrain(tempC, calib.fan_temp_min, calib.fan_temp_max),
                   calib.fan_temp_min, calib.fan_temp_max,
                   calib.fan_pwm_min, calib.fan_pwm_max);
  OCR1A = pwmRaw; // Directe duty cycle op D9
}
void readCouplers() {
  fwd.voltage = analogRead(FWD) * 5.0 / 1023 ;
  ref.voltage = analogRead(REV) * 5.0 / 1023 ;
  drv.voltage = analogRead(DRV) * 5.0 / 1023 ;
  fwd.power = max(0.0, calib.a_pwr + calib.b_pwr * fwd.voltage + calib.c_pwr * pow(fwd.voltage, 2));
  ref.power = max(0.0, calib.a_pwr + calib.b_pwr * ref.voltage + calib.c_pwr * pow(ref.voltage, 2));
  drv.power = max(0.0, a_drv + b_drv * drv.voltage + c_drv * pow(drv.voltage, 2));
  fwd.error = (fwd.power < calib.power_fwd_min) ? YES : NO;
  ref.error = (ref.power < calib.power_ref_min) ? YES : NO;
  drv.error = (drv.power < calib.power_drv_min || drv.power > calib.power_drv_max) ? YES : NO;
  if (!drv.error && (ref.power <= fwd.power ) ) {
    reflection_coefficient = sqrt(ref.power / fwd.power);
    swr = (1 + reflection_coefficient) / (1 - reflection_coefficient);
  } 
  else     swr = 0;
  AlarmState = (TxState && !fwd.error  && (ref.power <= fwd.power )  && swr > calib.swr_threshold) ;
}
void displayCouplers() {
  lcd.setCursor(0, 0);
  lcd.print(F("FWD     W  REF     W"));
  lcd.setCursor(4, 0);
  lcd.print((fwd.error == NO) ? dtos(fwd.power, 2, 0) : "<1");
  lcd.setCursor(16, 0);
  lcd.print((ref.error == NO) ? dtos(ref.power, 2, 0) : "<1");
  lcd.setCursor(0, 1);
  lcd.print(F("IN      W  SWR     "));
  lcd.setCursor(4, 1);
  lcd.print((drv.error == NO) ? dtos(drv.power, 3, 1) : "<");
  lcd.setCursor(15, 1);
  lcd.print(dtos(swr, 2, 1));
  lcd.setCursor(0, 3);
  updateProgressBar(fwd.power, calib.power_max, 3);
}
void displaySupply() {
  lcd.setCursor(0, 2);
  lcd.print(F("    V"));
  lcd.setCursor(0, 2);
  lcd.print(dtos(PSUvoltage, 3, 1));
  lcd.setCursor(8, 2);
  lcd.print(F("    A"));
  lcd.setCursor(8, 2);
  lcd.print(dtos(PSUcurrent, 3, 1));
  lcd.setCursor(15, 2);
  lcd.print(F("    C"));
  lcd.setCursor(15, 2);
  lcd.print(dtos(tempC, 3, 1));
}
void updateProgressBar(float value, float maxValue, int line) {
  int totalBlocks = 20; 
  float percentage = constrain(value / maxValue, 0.0, 1.0);
  int fullChars = percentage * totalBlocks;
  int partialChar = (percentage * totalBlocks - fullChars) * 5;
  for (int i = 0; i < totalBlocks; i++) {
    lcd.setCursor(i, line);
    if (i < fullChars) {
      lcd.write(byte(5)); // volledig blok
    } else if (i == fullChars && partialChar > 0) {
      lcd.write(byte(partialChar)); // gedeeltelijk blok
    } else {
      lcd.write(' '); // leeg
    }
  }
}
void alarm() {
  if (tempC >= calib.temp_alarm_threshold) {
    Serial.println("?? Temperatuur ALARM!");
    digitalWrite(ALARM_PIN, HIGH);
  }
  if (swr > calib.swr_threshold) {
    Serial.println("?? SWR ALARM!");    
    digitalWrite(ALARM_PIN, HIGH);
  }
  // Zet zender uit
  if (TxState && AlarmState) {
    digitalWrite(PApwr, LOW);
    delay(calib.RXdelay);
    digitalWrite(AntRelay, LOW);
    Serial.println("TX uitgeschakeld");
    TxState = false;
    TxEnable = false;     // Blokkeer nieuwe uitzendingen 
  }
  if (tempC < calib.temp_alarm_threshold) {
    Serial.println("Temperatuur weer normaal!");
    digitalWrite(ALARM_PIN, LOW);
    AlarmState = NO;
    TxEnable = true;
  }
  if (digitalRead(SWR_ResetButton) == LOW) { 
    Serial.println(F("Alarm reset"));
    digitalWrite(ALARM_PIN, LOW);
    AlarmState = NO;
    TxEnable = true;
  }
}
void showCalibration() {
  Serial.println(F("Calibratiewaarden:"));
  Serial.print(F("voltmeter_full_scale = ")); Serial.println(calib.voltmeter_full_scale);
  Serial.print(F("ampmeter_full_scale  = ")); Serial.println(calib.ampmeter_full_scale);
  Serial.print(F("power_drv_min        = ")); Serial.println(calib.power_drv_min);
  Serial.print(F("power_fwd_min        = ")); Serial.println(calib.power_fwd_min);
  Serial.print(F("power_ref_min        = ")); Serial.println(calib.power_ref_min);
  Serial.print(F("power_drv_max        = ")); Serial.println(calib.power_drv_max);
  Serial.print(F("TXdelay              = ")); Serial.println(calib.TXdelay);
  Serial.print(F("RXdelay              = ")); Serial.println(calib.RXdelay);
  Serial.print(F("temp_alarm_threshold = ")); Serial.println(calib.temp_alarm_threshold);
  Serial.print(F("SWR_threshold        = ")); Serial.println(calib.swr_threshold);
  Serial.print(F("fan_temp_min         = ")); Serial.println(calib.fan_temp_min);
  Serial.print(F("fan_temp_max         = ")); Serial.println(calib.fan_temp_max);
  Serial.print(F("fan_pwm_min          = ")); Serial.println(calib.fan_pwm_min);
  Serial.print(F("fan_pwm_max          = ")); Serial.println(calib.fan_pwm_max);
  Serial.print(F("power_max            = ")); Serial.println(calib.power_max);
  Serial.print(F("Bvalue               = ")); Serial.println(calib.Bvalue);
  Serial.print(F("Rref               = ")); Serial.println(calib.Rref);
  Serial.print(F("a_pwr coefficient    = ")); Serial.println(calib.a_pwr); 
  Serial.print(F("b_pwr coefficient    = ")); Serial.println(calib.b_pwr); 
  Serial.print(F("c_pwr coefficient    = ")); Serial.println(calib.c_pwr);
}
void setCalibration(String cmd) {
  cmd.remove(0, 4); // haal "SET " weg
  int sep = cmd.indexOf('=');
  if (sep == -1) return;
  String key = cmd.substring(0, sep);
  String val = cmd.substring(sep + 1);
  val.trim();
  if      (key == "voltmeter") calib.voltmeter_full_scale = val.toFloat();
  else if (key == "ampmeter") calib.ampmeter_full_scale = val.toFloat();
  else if (key == "drvmin") calib.power_drv_min = val.toFloat();
  else if (key == "fwdmin") calib.power_fwd_min = val.toInt();
  else if (key == "refmin") calib.power_ref_min = val.toInt();
  else if (key == "drvmax") calib.power_drv_max = val.toInt();
  else if (key == "txdelay") calib.TXdelay = val.toInt();
  else if (key == "rxdelay") calib.RXdelay = val.toInt();
  else if (key == "tempalarm") calib.temp_alarm_threshold = val.toFloat();
  else if (key == "swralarm") calib.swr_threshold = val.toInt();
  else if (key == "fan_temp_min") calib.fan_temp_min = val.toFloat();
  else if (key == "fan_temp_max") calib.fan_temp_max = val.toFloat();
  else if (key == "fan_pwm_min") calib.fan_pwm_min = val.toInt();
  else if (key == "fan_pwm_max") calib.fan_pwm_max = val.toInt();
  else if (key == "power_max") calib.power_max = val.toFloat();
  else if (key == "Bvalue") calib.Bvalue = val.toFloat();
  else if (key == "Rref") calib.Rref = val.toFloat();
  else if (key == "a_pwr") calib.a_pwr = val.toFloat();
  else if (key == "b_pwr") calib.b_pwr = val.toFloat();
  else if (key == "c_pwr") calib.c_pwr = val.toFloat();
  else {
    Serial.println(F("Onbekende parameter"));
    return;
  }
  EEPROM.put(calibEEPROMAddr, calib);
  Serial.println(F("Calibratie bijgewerkt."));
  showCalibration();
}
void showHelp() {
  Serial.println(F("?? Beschikbare commando's:"));
  Serial.println(F("HELP              ? Toon dit overzicht"));
  Serial.println(F("DEBUG ON          ? Zet seriële logging aan"));
  Serial.println(F("DEBUG OFF         ? Zet seriële logging uit"));
  Serial.println(F("SHOWCAL           ? Toon huidige calibratiewaarden"));
  Serial.println(F("RESETCAL          ? Wis alle calibratiewaarden"));
  Serial.println(F("SET voltmeter=X   ? Stel voltmeter schaal (float)"));
  Serial.println(F("SET ampmeter=X    ? Stel ampmeter schaal (float)"));
  Serial.println(F("SET drvmin=X      ? Minimaal driver vermogen (float)"));
  Serial.println(F("SET fwdmin=X      ? Minimaal forward vermogen (int)"));
  Serial.println(F("SET refmin=X      ? Minimaal reflected vermogen (int)"));
  Serial.println(F("SET drvmax=X      ? Maximaal driver vermogen (int)"));
  Serial.println(F("SET txdelay=X     ? Delay bij zenden (ms, int)"));
  Serial.println(F("SET rxdelay=X     ? Delay bij ontvangst (ms, int)"));
  Serial.println(F("SET tempalarm=X   ? Temperatuurgrens alarm (float °C)"));
  Serial.println(F("SET swralarm=X    ? SWR grens alarm (float °C)"));
  Serial.println(F("SET fan_temp_min=X? min temp fan on (float °C)"));
  Serial.println(F("SET fan_temp_max=X? max temp fan on (float °C)"));
  Serial.println(F("SET fan_pwm_min=X ? min pwm fan on (byte)"));
  Serial.println(F("SET fan_pwm_max=X ? max pwm fan on (byte)"));
  Serial.println(F("SET power_max=X   ? max power (float)"));
  Serial.println(F("SET Bvalue=X      ? Steinhart B (float)"));
  Serial.println(F("SET Rref=X        ? R reference (float)"));
  Serial.println(F("SET a_pwr=X       ? a_pwr coefficient (float)"));
  Serial.println(F("SET b_pwr=X       ? b_pwr coefficient (float)"));
  Serial.println(F("SET c_pwr=X       ? c_pwr coefficient (float)"));
  Serial.println(F(""));
  Serial.println(F("Voorbeeld: SET tempalarm=65.5"));
}
void Debugging() {
    Serial.print(PSUvoltage);Serial.print(",");
    Serial.print(PSUcurrent);Serial.print(",");
    Serial.print(tempC);Serial.print(",");
    Serial.print(fwd.power); Serial.print(",");
    Serial.print(ref.power); Serial.print(",");
    Serial.print(drv.power); Serial.print(",");
    Serial.print(fwd.voltage); Serial.print(",");
    Serial.print(fwd.error); Serial.print(",");
    Serial.print(swr); Serial.print(","); 
    Serial.print(pwmValue); Serial.print(","); 
    Serial.println();
}
void resetCalibrationEEPROM() {
  int CALIBRATION_START_ADDR = 0;
  for (int i = 0; i < sizeof(Calibration); i++) {
    EEPROM.write(CALIBRATION_START_ADDR + i, 0);
  }
}drive
forward
reflected
FAN
Alarm
PTT
Vd on
Relays
reset alarm