/* Latest running code on the Arduino in the Pontiac as of 06.06.2024 */
/* Added watchdog timer, 4 seconds. */

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
//#include <SoftwareSerial.h>
#include <avr/wdt.h>

#define i2c_Address 0x3C // OLED Adress.
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

int M3200address = 0x28;   // 0x28, 0x36, or 0x46, depending on the sensor.
float maxPressure = 100;   // pressure in PSI for this sensor, 100, 250, 500, ... 10k.
float pressureO;
float temperatureO;
byte status;

float tempForFanStartup = 85.0; // 175 target low temp. below this temperature, the fan will be off
float tempForFanOnFull = 100.0; // 210 target High temp. above this temperature, the fan will be on at full power

float voltsForFanStartup = 2.0; // Roughly the signal voltage that triggers the slowest speed.

const int fanPwmOutPin = 12; // Arduino forces this pin to 0 or 5 volts.
const int tempSensorPin = 7; // Pin to read analog voltage from the temp sensor.
int pwmDuty; // The calculated PWM duty is stored here
float pwmMinStartupDuty; // the starting duty is stored here (mazda module starts fans at about 24 % duty)
float currTemperature; // the temperature in F is stored here
float fanSpeedPct;

OneWire oneWire1 (tempSensorPin);
DallasTemperature sensorClt1(&oneWire1);

/*const byte rxPin = 2;
const byte txPin = 3;
SoftwareSerial secondSerial (rxPin, txPin);*/

Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

unsigned long previousMillis = 0;  // Variable to store the last time the serial data was sent
const long interval = 5000;  // Interval between serial data transmissions (in milliseconds)

unsigned long pressureMillis = 0;
const int pressureTiming = 1000; // 500ms every data acquisition

void setup() {
//wdt_enable(WDTO_4S);

Serial.begin(9600); // set up serial port for 115200 baud (optional)

Serial.println("Setup start...");

Wire.begin();
delay(2000);

  if (!display.begin(i2c_Address)) {
    Serial.println(F("SH110X allocation failed"));
    for (;;);
  }
  display.clearDisplay(); // Clear the display buffer

Serial.println("Starting Dallas DS1820...");
sensorClt1.begin();
sensorClt1.requestTemperatures();

currTemperature = sensorClt1.getTempCByIndex(0);
pwmMinStartupDuty = (voltsForFanStartup / 5.0) * 255.0; // convert the Mazda starting voltage to a PWM duty

//Serial.println("Starting softSerial...");
/*secondSerial.begin(9600);
delay(500);
secondSerial.print("F20.0");
delay(20);
secondSerial.print("D000");*/
Serial.println("Setup done, running loop...");
}

void loop() {
unsigned long currentMillis = millis();
unsigned long pressureMillis = millis();


  if (currentMillis - previousMillis >= pressureTiming) {
    get_data_from_M3200();
    previousMillis = currentMillis;
  }
  

readAndTranslateTempSensor();
calculate_and_send_PWM();
//print_to_serial_port(); // uncomment this line for testing and calibration to the laptop.

  // Check if it's time to send serial data
/*  if (currentMillis - previousMillis >= interval) {
      serial_to_signal_generator();  // Send serial data
      previousMillis = currentMillis;  // Save the last time serial data was sent
  }*/
displayOutput();
//wdt_reset();
}

void calculate_and_send_PWM() { /* ++++++++ subroutine to calculate PWM duty cycle ++++++++++++++*/

if (currTemperature < tempForFanStartup) { // If the temperature is below the lowest setpoint, turn fan off
//analogWrite(fanPwmOutPin, 0); // PWM duty = 0 percent
pwmDuty = 0;
return;
}

if (currTemperature > tempForFanOnFull) { // If the temperature is above the highest setpoint, turn fan on full
//analogWrite(fanPwmOutPin, 255); // PWM duty = 100 percent
pwmDuty = 255;
return;
}

float tempRange = tempForFanOnFull - tempForFanStartup ; // start calculating duty cycle
float pwmRange = 255.0 - pwmMinStartupDuty ;
float pwmDutyPct = (currTemperature - tempForFanStartup) / tempRange ;

pwmDuty = (int) (pwmMinStartupDuty + (pwmDutyPct * pwmRange) +.5); // actual PWM duty is calculated here

fanSpeedPct = map(pwmDuty, 0, 255, 0, 100);

if (pwmDuty > 255) { // make sure duty ended up between 255 and 0
pwmDuty = 255;
}
if (pwmDuty < 0) {
pwmDuty = 0;
}

//analogWrite(fanPwmOutPin, pwmDuty); // write PWM duty to PWM output pin

} // end calculate_and_send_PWM

void readAndTranslateTempSensor() {
    // Request temperature readings
    sensorClt1.requestTemperatures();
    
    // Read the temperature from the sensor
    float tempC = sensorClt1.getTempCByIndex(0);

    // Check if the temperature reading is within the valid range
    if (tempC > -30 && tempC < 130) {
        // If the reading is within the valid range, update the current temperature
        currTemperature = tempC;
    }
    // If the reading is outside the valid range, do not update the current temperature
}

void displayOutput() {
    display.clearDisplay();

    display.setTextSize(1);
    display.setTextColor(SH110X_WHITE);

    // Display temperature on the screen in Celsius with one decimal place
    display.setCursor(0, 0);
    display.print("Temp:");
    display.setCursor(30, 0);
    display.print(currTemperature, 1); // Display one decimal place

    // Calculate and display fan speed percentage
    if (currTemperature < tempForFanStartup) {
        fanSpeedPct = 0; // Set fan speed to 0% if temperature is below startup threshold
    } else {
        fanSpeedPct = map(pwmDuty, 0, 255, 0, 100); // Calculate fan speed percentage
    }

    display.setCursor(0, 10);
    display.print("Fan%:");
    display.setCursor(30, 10);
    display.print((int)fanSpeedPct);

    display.setCursor(0, 30);
    display.print("PrsO:");
    display.setCursor(30, 30);
    display.print(pressureO);

    display.setCursor(0, 40);
    display.print("TmpO:");
    display.setCursor(30, 40);
    display.print(temperatureO);
    // Update the display
    display.display();
}

void print_to_serial_port() { /* ++++++++++ optional prints values to laptop usb port for debugging and calibration ++*/
Serial.print(F("currTemperature C: ")); Serial.print(currTemperature);
Serial.print(F(" pwmDuty: ")); Serial.println(pwmDuty);
Serial.print(F("Percentage: ")); Serial.println((int)fanSpeedPct);
}

/*void serial_to_signal_generator() {
    // Determine fan speed percentage to send
    int speedToSend = (int)fanSpeedPct;

    // Ensure fan speed is 0 if temperature is below startup threshold
    if (currTemperature < tempForFanStartup) {
        speedToSend = 0;
    }

    // Check the length of fanSpeedPct
    if (speedToSend < 10) {
        secondSerial.print(F("D00")); // Add "D00" prefix for single-digit fan speed
    } else if (speedToSend < 100) {
        secondSerial.print(F("D0")); // Add "D0" prefix for double-digit fan speed
    } else {
        secondSerial.print(F("D")); // Add "D" prefix for triple-digit fan speed
    }
    
    secondSerial.print(speedToSend); // Send fan speed
}*/

void get_data_from_M3200() {
    Wire.requestFrom(M3200address, 4);   // request 4 bytes
    int n = Wire.available();

    if (n == 4) {
      status = 0;
      uint16_t rawP;     // pressure data from sensor
      uint16_t rawT;     // temperature data from sensor
      rawP = (uint16_t) Wire.read();    // upper 8 bits
      rawP <<= 8;
      rawP |= (uint16_t) Wire.read();    // lower 8 bits
      rawT = (uint16_t) Wire.read();    // upper 8 bits
      rawT <<= 8;
      rawT |= (uint16_t) Wire.read();   // lower 8 bits

      status = rawP >> 14;   // The status is 0, 1, 2, or 3
      rawP &= 0x3FFF;   // keep 14 bits, remove status bits

      rawT >>= 5;     // the lowest 5 bits are not used

      float pressure = ((rawP - 1000.0) / (15000.0 - 1000.0)) * maxPressure;
      float temperature = ((rawT - 512.0) / (1075.0 - 512.0)) * 55.0;
      
      temperatureO = temperature;
      pressureO = pressure;

      Serial.println("--- Values from M3200 ---");
      Serial.println(temperature);
      Serial.println(pressure);



    } else {
      Serial.println("M3200 Pressure Transducer not Detected");
    }
}