author | Eric Wertz <ericdwertz@gmail.com> | |
Fri, 20 Jan 2023 16:38:36 +0000 (11:38 -0500) | ||
committer | Eric Wertz <ericdwertz@gmail.com> | |
Fri, 20 Jan 2023 16:38:36 +0000 (11:38 -0500) |
Makefile | [new file with mode: 0644] | patch | blob |
fancom.sh | [new file with mode: 0755] | patch | blob |
fanduino.ino | [new file with mode: 0644] | patch | blob |
diff --git a/Makefile b/Makefile
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+ALTERNATE_CORE_PATH = /usr/share/arduino/hardware/archlinux-arduino/avr
+USER_LIB_PATH = /home/eric/fanduino
+ARDUINO_DIR = /usr/share/arduino/hardware/archlinux-arduino/avr
+ARDMK_DIR = /usr/share/arduino
+AVR_TOOLS_DIR = /usr
+
+ARDUINO_LIBS = Wire
+#BOARD_TAG = nano
+BOARD_SUB = 16MHzatmega328
+MONITOR_PORT = /dev/ttyUSB0
+#CFLAGS_STD = -std=gnu11 -flto -fno-fat-lto-objects
+#CXXFLAGS_STD = -std=gnu++11 -fno-threadsafe-statics -flto
+CXXFLAGS += -fno-devirtualize
+
+AVRDUDE = /usr/bin/avrdude
+AVRDUDE_CONF = /etc/avrdude.conf
+
+include /usr/share/arduino/Arduino.mk
diff --git a/fancom.sh b/fancom.sh
--- /dev/null
+++ b/fancom.sh
@@ -0,0 +1,53 @@
+#! /bin/bash
+
+hdparm -B 100 /dev/sda
+hdparm -B 100 /dev/sdb
+
+hddtemp -d /dev/sda /dev/sdb
+
+#stty -F /dev/ttyUSB0 115200 cs8 cread clocal
+killall tail
+avrdude -q -V -p atmega328p -C /etc/avrdude.conf -D -c arduino -b 115200 -P /dev/ttyUSB0 -U flash:r:/tmp/fancom_firmware.hex:i
+#avrdude -q -V -p atmega328p -C /etc/avrdude.conf -D -c arduino -b 115200 -P /dev/ttyUSB0 -U flash:w:/tmp/fancom_firmware.hex:i
+ard-reset-arduino /dev/ttyUSB0
+sleep 3
+tail -f /dev/ttyUSB0 >> /var/log/fancom.log &
+
+purgecount=0
+hddupdate=0
+cputemp=0
+hddtemp=0
+outtemp=0
+while true
+do
+ cputemp=`cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}' RS="\n"`
+ #sensors -u | sed -rn 's/.*temp1_input: ([0-9.]+)/\1/p' | awk '{s+=$1}END{print "$",s/NR}' RS="\n" > /dev/ttyUSB0
+
+ if [ $hddtemp != 0 ]; then
+ if [ $hddtemp > $cputemp ]; then
+ outtemp=`echo $cputemp`
+ else
+ outtemp=`echo $hddtemp`
+ fi
+ #outtemp=`echo $hddtemp $cputemp | awk '{print ($1+$2)/2}'`
+ else
+ outtemp=`echo $cputemp`
+ fi
+
+ echo $ $outtemp >> /dev/ttyUSB0
+
+ ((purgecount++))
+ ((hddupdate++))
+ if [ $purgecount == 3600 ]; then
+ purgecount=0
+ kill $!
+ tail -n 3600 /var/log/fancom.log > /var/log/fancom.log.new
+ cat /var/log/fancom.log.new > /var/log/fancom.log
+ rm /var/log/fancom.log.new
+ tail -f /dev/ttyUSB0 >> /var/log/fancom.log &
+ fi
+ if [ $hddupdate == 30 ]; then
+ hddtemp=`nc localhost 7634 | awk -F '|' '{print (($4=="SLP"?0:$4)+($9=="SLP"?0:$9))/2*1.28}'`
+ fi
+ sleep 1
+done
diff --git a/fanduino.ino b/fanduino.ino
--- /dev/null
+++ b/fanduino.ino
@@ -0,0 +1,240 @@
+#include <math.h>
+
+#define MATH_E 2.718281f
+
+#define TARGET_TEMP_CPU 60.0f
+#define TARGET_TEMP_HEATSINK 42.0f
+#define TARGET_TEMP_HEATSINK_MAX 42.0f
+#define TEMP_PERIOD 3
+
+#define TEMP_R1 10000
+#define TEMP_SENSOR 10000
+#define TEMP_SENSOR_NOMINAL 25
+#define TEMP_SENSOR_BCOEFFICIENT 3435
+#define TEMP_PIN A7
+
+#define FALLBACK_TIMEOUT 10
+
+#define FAN_SPINDOWN_MAX 5.0f/320.0f
+
+float fanSpeed;
+float fanInterval;
+float deltaTSerial;
+float deltaTSensor;
+float targetDeltaT;
+float serialTemp;
+float sensorTemp;
+float previousTempSerial;
+float previousTempSensor;
+float tAdjust;
+float currentTemp;
+float targetTemp;
+
+unsigned int lastSerialRead;
+
+unsigned long currentTick;
+unsigned long previousTick;
+
+//configure Timer 1 (pins 9,10) to output 25kHz PWM
+void setupTimer1(){
+ //Set PWM frequency to about 25khz on pins 9,10 (timer 1 mode 10, no prescale, count to 320)
+ TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
+ TCCR1B = (1 << CS10) | (1 << WGM13);
+ ICR1 = 320;
+ OCR1A = 0;
+ OCR1B = 0;
+}
+//equivalent of analogWrite on pin 9
+void setFanSpeed()
+{
+ fanSpeed=fanSpeed<0?0:fanSpeed>1?1:fanSpeed;
+ OCR1A = (uint16_t)(320*fanSpeed);
+}
+
+void writeCSVLine( float* values, int count )
+{
+ char buffer[10];
+ for( int i = 0; i < count; i++ )
+ {
+ dtostrf( values[i], 4, 4, buffer );
+ Serial.write( buffer, strlen(buffer) );
+ if( i != count - 1 )
+ Serial.write( ", ", 1 );
+ }
+ Serial.write( "\r\n", 2 );
+}
+
+float movingAverage( float avg, float val, float period )
+{
+ float w = 1.0f/period;
+ return (avg*(1.0f-w))+(val*w);
+}
+
+#define BUF_MAX 16
+void readSerialTemp()
+{
+ char buffer[BUF_MAX];
+ float val = 0;
+ int i;
+ char r;
+
+ i = 0;
+ r = Serial.read();
+ while( r != -1 && i < BUF_MAX - 1 )
+ {
+ buffer[i] = r;
+ i++;
+ r = Serial.read();
+ }
+ if( buffer[0] == '$' && buffer[i-1] == '\n' )
+ {
+ buffer[i-1] = '\0';
+ val = atof( buffer + 1 );
+ }
+
+ if( val != 0 )
+ {
+ lastSerialRead = 0;
+
+ if( serialTemp == 0.0f )
+ serialTemp = val;
+ else
+ serialTemp = movingAverage( serialTemp, val, TEMP_PERIOD );
+
+ if( tAdjust == 0.0f )
+ tAdjust = serialTemp / sensorTemp;
+ }
+ else
+ lastSerialRead++;
+}
+
+void readSensorTemp()
+{
+ float reads = 0;
+ float reading;
+
+ for( int i=0; i<10; i++ )
+ {
+ reads += analogRead( TEMP_PIN );
+ delay( 10 );
+ }
+
+ reading = reads / 10;
+ reading = ( 1023 / reading ) - 1;
+ reading = TEMP_R1 / reading;
+
+ float steinhart;
+ steinhart = reading / TEMP_SENSOR; // (R/Ro)
+ steinhart = log(steinhart); // ln(R/Ro)
+ steinhart /= TEMP_SENSOR_BCOEFFICIENT; // 1/B * ln(R/Ro)
+ steinhart += 1.0 / (TEMP_SENSOR_NOMINAL + 273.15); // + (1/To)
+ steinhart = 1.0 / steinhart; // Invert
+ steinhart -= 273.15; // convert to C
+
+ if( sensorTemp == 0.0f )
+ sensorTemp = steinhart;
+ else
+ sensorTemp = movingAverage( sensorTemp, steinhart, TEMP_PERIOD );
+}
+
+void calculateAdjustment()
+{
+ float adjust;
+
+ adjust = powf( MATH_E, ( serialTemp / sensorTemp ) ) - sensorTemp;
+ tAdjust = movingAverage( tAdjust, adjust, 60 );
+}
+
+void setup()
+{
+ Serial.begin( 115200 );
+ //enable outputs for Timer 1
+ pinMode(9,OUTPUT); //1A
+ pinMode(10,OUTPUT); //1B
+
+ setupTimer1();
+ //example...
+ setFanSpeed(); //set duty to 50% on pin 9
+ fanSpeed = 0;
+ fanInterval = 1.0f / 320.0f;
+ serialTemp = 0;
+ sensorTemp = 0;
+ previousTempSerial = 0;
+ previousTempSensor = 0;
+ deltaTSerial = 0;
+ deltaTSensor = 0;
+ tAdjust = 0;
+ lastSerialRead = 0;
+ targetTemp = TARGET_TEMP_HEATSINK;
+}
+
+void loop()
+{
+ float outVals[10];
+ float deltaT;
+
+ float sensorDiff, serialDiff;
+
+ currentTick = millis()/1000;
+ if( currentTick != previousTick )
+ {
+ readSensorTemp();
+ readSerialTemp();
+
+ if( serialTemp != 0.0f )
+ calculateAdjustment();
+
+ deltaTSensor = movingAverage( deltaTSensor, sensorTemp - previousTempSensor, 3 );
+ deltaTSerial = movingAverage( deltaTSerial, serialTemp - previousTempSerial, 3 );
+
+ if( lastSerialRead < FALLBACK_TIMEOUT )
+ {
+ sensorDiff = TARGET_TEMP_HEATSINK_MAX - sensorTemp;
+ serialDiff = TARGET_TEMP_CPU - serialTemp;
+ if( serialDiff < sensorDiff )
+ {
+ currentTemp = serialTemp;
+ targetTemp = TARGET_TEMP_CPU;
+ deltaT = deltaTSerial;
+ }
+ else
+ {
+ currentTemp = sensorTemp;
+ targetTemp = TARGET_TEMP_HEATSINK_MAX;
+ deltaT = deltaTSensor;
+ }
+ }
+ else
+ {
+ currentTemp = sensorTemp;
+ targetTemp = TARGET_TEMP_HEATSINK;
+ deltaT = deltaTSensor;
+ }
+
+
+ targetDeltaT = (targetTemp - currentTemp) / 60.0f;
+ fanInterval = max( 1.0f / 320.0f, abs( deltaT - targetDeltaT ) * 0.25f );
+
+ if( targetDeltaT < deltaT )
+ fanSpeed += fanInterval;
+ else if( targetDeltaT > deltaT )
+ fanSpeed -= fanInterval > FAN_SPINDOWN_MAX ? FAN_SPINDOWN_MAX : fanInterval;
+
+ setFanSpeed();
+
+ outVals[0] = serialTemp;
+ outVals[1] = sensorTemp;
+ outVals[2] = currentTemp;
+ outVals[3] = targetTemp;
+ outVals[4] = deltaT;
+ outVals[5] = targetDeltaT;
+ outVals[6] = fanInterval;
+ outVals[7] = fanSpeed;
+ writeCSVLine( outVals, 8 );
+
+ previousTick = currentTick;
+ previousTempSerial = serialTemp;
+ previousTempSensor = sensorTemp;
+ }
+
+}