#define IS_SIMULATION true
#include <TimerOne.h>
const int cfgFan1ControlPinFromIC = A0;
const int cfgFan1FeedbackPinToIC = 2;
const int cfgFan1ControlPinToFan = 3;
const int cfgFan1Mapping[] = { 21700, 25, 65 }; // Fan RPM Max, Remapped Duty Min, Remapped Duty Max
/*
const int cfgFan2ControlPinFromIC = A1;
const int cfgFan2FeedbackPinToIC = -1;
const int cfgFan2ControlPinToFan = -1;
const int cfgFan2Mapping[] = { 21700, 25, 65 }; // Fan RPM Max, Remapped Duty Min, Remapped Duty Max
const int cfgFan3ControlPinFromIC = A2;
const int cfgFan3FeedbackPinToIC = -1;
const int cfgFan3ControlPinToFan = -1;
const int cfgFan3Mapping[] = { 21700, 25, 65 }; // Fan RPM Max, Remapped Duty Min, Remapped Duty Max
const int cfgFan4ControlPinFromIC = A3;
const int cfgFan4FeedbackPinToIC = -1;
const int cfgFan4ControlPinToFan = -1;
const int cfgFan4Mapping[] = { 21700, 25, 65 }; // Fan RPM Max, Remapped Duty Min, Remapped Duty Max
*/
void setup() {
Serial.begin(115200);
Timer1.initialize(40); // 25 kHz
}
void loop() {
float fan1DutyCycleFromIC = readDutyCycleFromPin(cfgFan1ControlPinFromIC);
float fan1RPMExpectedFromIC = mapFloat(fan1DutyCycleFromIC, 0, 100, 0, cfgFan1Mapping[0]);
float fan1FeedbackToIC = calculateFrequencyForSpecificRPM(fan1RPMExpectedFromIC);
float fan1DutyCycleToFan = mapFloat(fan1DutyCycleFromIC, 0, 100, cfgFan1Mapping[1], cfgFan1Mapping[2]);
float fan1RPMExpectedToFan = mapFloat(fan1DutyCycleToFan, 0, 100, 0, cfgFan1Mapping[0]);
/*
float fan2DutyCycleFromIC = readDutyCycleFromPin(cfgFan2ControlPinFromIC);
float fan2RPMExpectedFromIC = mapFloat(fan2DutyCycleFromIC, 0, 100, 0, cfgFan2Mapping[0]);
float fan2FeedbackToIC = calculateFrequencyForSpecificRPM(fan2RPMExpectedFromIC);
float fan2DutyCycleToFan = mapFloat(fan2DutyCycleFromIC, 0, 100, cfgFan2Mapping[1], cfgFan2Mapping[2]);
float fan2RPMExpectedToFan = mapFloat(fan2DutyCycleToFan, 0, 100, 0, cfgFan2Mapping[0]);
float fan3DutyCycleFromIC = readDutyCycleFromPin(cfgFan3ControlPinFromIC);
float fan3RPMExpectedFromIC = mapFloat(fan3DutyCycleFromIC, 0, 100, 0, cfgFan3Mapping[0]);
float fan3FeedbackToIC = calculateFrequencyForSpecificRPM(fan3RPMExpectedFromIC);
float fan3DutyCycleToFan = mapFloat(fan3DutyCycleFromIC, 0, 100, cfgFan3Mapping[1], cfgFan3Mapping[2]);
float fan3RPMExpectedToFan = mapFloat(fan3DutyCycleToFan, 0, 100, 0, cfgFan3Mapping[0]);
float fan4DutyCycleFromIC = readDutyCycleFromPin(cfgFan4ControlPinFromIC);
float fan4RPMExpectedFromIC = mapFloat(fan4DutyCycleFromIC, 0, 100, 0, cfgFan4Mapping[0]);
float fan4FeedbackToIC = calculateFrequencyForSpecificRPM(fan4RPMExpectedFromIC);
float fan4DutyCycleToFan = mapFloat(fan4DutyCycleFromIC, 0, 100, cfgFan4Mapping[1], cfgFan4Mapping[2]);
float fan4RPMExpectedToFan = mapFloat(fan4DutyCycleToFan, 0, 100, 0, cfgFan4Mapping[0]);
*/
#if !IS_SIMULATION
if (cfgFan1FeedbackPinToIC != -1) tone(cfgFan1FeedbackPinToIC, fan1FeedbackToIC);
if (cfgFan1ControlPinToFan != -1) Timer1.pwm(cfgFan1ControlPinToFan, (fan1DutyCycleToFan / 100) * 1023);
/*
if (cfgFan2FeedbackPinToIC != -1) tone(cfgFan2FeedbackPinToIC, fan2FeedbackToIC);
if (cfgFan2ControlPinToFan != -1) Timer1.pwm(cfgFan2ControlPinToFan, (fan2DutyCycleToFan / 100) * 1023);
if (cfgFan3FeedbackPinToIC != -1) tone(cfgFan3FeedbackPinToIC, fan3FeedbackToIC);
if (cfgFan3ControlPinToFan != -1) Timer1.pwm(cfgFan3ControlPinToFan, (fan3DutyCycleToFan / 100) * 1023);
if (cfgFan4FeedbackPinToIC != -1) tone(cfgFan4FeedbackPinToIC, fan4FeedbackToIC);
if (cfgFan4ControlPinToFan != -1) Timer1.pwm(cfgFan4ControlPinToFan, (fan4DutyCycleToFan / 100) * 1023);
*/
#endif
#if IS_SIMULATION
Serial.print("Fan 1\tIC DC: ");
Serial.print((int)fan1DutyCycleFromIC);
Serial.print("%\tIC RPM EXP: ");
Serial.print((int)fan1RPMExpectedFromIC);
Serial.print("\tIC FB EXP: ");
Serial.print(fan1FeedbackToIC);
Serial.print(" Hz\tFan DC: ");
Serial.print((int)fan1DutyCycleToFan);
Serial.print("%\tFan RPM EXP: ");
Serial.println((int)fan1RPMExpectedToFan);
/*
Serial.print("Fan 2\tIC DC: ");
Serial.print((int)fan2DutyCycleFromIC);
Serial.print("%\tIC RPM EXP: ");
Serial.print((int)fan2RPMExpectedFromIC);
Serial.print("\tIC FB EXP: ");
Serial.print(fan2FeedbackToIC);
Serial.print(" Hz\tFan DC: ");
Serial.print((int)fan2DutyCycleToFan);
Serial.print("%\tFan RPM EXP: ");
Serial.println((int)fan2RPMExpectedToFan);
Serial.print("Fan 3\tIC DC: ");
Serial.print((int)fan3DutyCycleFromIC);
Serial.print("%\tIC RPM EXP: ");
Serial.print((int)fan3RPMExpectedFromIC);
Serial.print("\tIC FB EXP: ");
Serial.print(fan3FeedbackToIC);
Serial.print(" Hz\tFan DC: ");
Serial.print((int)fan3DutyCycleToFan);
Serial.print("%\tFan RPM EXP: ");
Serial.println((int)fan3RPMExpectedToFan);
Serial.print("Fan 4\tIC DC: ");
Serial.print((int)fan4DutyCycleFromIC);
Serial.print("%\tIC RPM EXP: ");
Serial.print((int)fan4RPMExpectedFromIC);
Serial.print("\tIC FB EXP: ");
Serial.print(fan4FeedbackToIC);
Serial.print(" Hz\tFan DC: ");
Serial.print((int)fan4DutyCycleToFan);
Serial.print("%\tFan RPM EXP: ");
Serial.println((int)fan4RPMExpectedToFan);
*/
#endif
#if IS_SIMULATION
delay(100);
#else
delay(50);
#endif
}
float readDutyCycleFromPin(int pin) { // In simulation it will use a analog pin instead of a real PWM signal
#if IS_SIMULATION
return (float)mapFloat(analogRead(pin), 0, 1023, 0, 100);
#else
// https://forum.arduino.cc/t/reading-pwm-duty-cycle/258521
unsigned long highTime = pulseIn(pin, HIGH);
unsigned long lowTime = pulseIn(pin, LOW);
return (float)highTime / float(highTime + lowTime);
#endif
}
int calculateFrequencyForSpecificRPM(int rpm) {
return rpm / 30;
}
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}