/* * ATtiny85 Fan Controller * * Target: ATtiny85 @ 8MHz * * Schematic: * ATtiny85 * +---v---+ * RST 1| |8 VCC * (NC) 2| |7 PB2 (POT - Target Temp) * (NC) 3| |6 PB1 (PWM - Fan Ctrl) * GND 4| |5 PB0 (DS18B20 Data) * +-------+ */ #include #include #include // --- Pin Definitions --- // PB1 (MISO/DO/OC1A) - PWM Output // PB2 (ADC1/SCK/USCK/SCL) - Potentiometer Input // PB0 (MOSI/DI/SDA) - DS18B20 Data Line #define PIN_PWM 1 // PB1 #define PIN_POT A1 // PB2 is Analog Input 1 #define PIN_ONE_WIRE 0 // PB0 // --- Constants --- #define PWM_TOP 159 // For 25kHz with Prescaler 2 (8MHz / 2 / (159+1) = 25kHz) // --- Globals --- OneWire oneWire(PIN_ONE_WIRE); DallasTemperature sensors(&oneWire); // Temperature Curve // We will define a simple curve: // Below MIN_TEMP -> Min Duty // Above MAX_TEMP -> Max Duty // In between -> Linear const float MIN_TEMP = 25.0; // Start ramping up here const float MAX_TEMP = 50.0; // Full speed here const uint8_t MIN_DUTY = 32; // ~20% duty cycle (32/159) - Fan start const uint8_t MAX_DUTY = 159; // 100% duty cycle const float CONTROL_BAND = 10.0; // 10 degrees band, in this band around target temp the fan speed is linearly interpolated // Potentiometer Mapping Constants const int POT_MIN_TEMP = 20; // 0 on ADC maps to this temp const int POT_MAX_TEMP = 60; // 1023 on ADC maps to this temp void setupPWM() { // Configure Timer1 for PWM on PB1 (OC1A) // We want ~25kHz. // System Clock = 8MHz. // Timer1 is 8-bit. // Prescaler = 2 -> Timer Clock = 4MHz. // OCR1C (Top) = 159 -> Freq = 4MHz / 160 = 25kHz. // Clear Timer on Compare Match (CTC) mode is not exactly what we want for PWM // typically. We want Pulse Width Modulator A Enable (PWM1A). // TCCR1: // CTC1 = 0 // PWM1A = 1 (Enable PWM A) // COM1A1:0 = 10 (Clear OC1A on Compare Match, Set on 0x00) -> Non-Inverted PWM // CS13:10 = 0010 (Prescaler = 2) TCCR1 = 0; TCCR1 |= (1 << PWM1A) | (1 << COM1A1) | (1 << CS11); // OCR1C sets the TOP value (Frequency) OCR1C = PWM_TOP; // Initial Duty Cycle 0 OCR1A = 0; // Set PB1 as Output pinMode(PIN_PWM, OUTPUT); } void setup() { setupPWM(); // Initialize Sensors sensors.begin(); // Initialize Potentiometer Pin pinMode(PIN_POT, INPUT); // Not strictly necessary for AnalogRead but good practice } void loop() { // the Pot maps to a target temperature range int potValue = analogRead(PIN_POT); float targetTemp = map(potValue, 0, 1023, POT_MIN_TEMP, POT_MAX_TEMP); // Read Current Temp sensors.requestTemperatures(); float currentTemp = sensors.getTempCByIndex(0); // Handle error reading if (currentTemp == DEVICE_DISCONNECTED_C) { // Safe fail: Full speed OCR1A = MAX_DUTY; delay(1000); return; } // If current < target -> Min Speed (or Off) // If current > target + band -> Max Speed // Linear in betwen int newDuty = 0; if (currentTemp < targetTemp) { newDuty = MIN_DUTY; // Keep it running at min speed or 0? // Let's say Min Speed to keep airflow. } else if (currentTemp >= targetTemp + CONTROL_BAND) { newDuty = MAX_DUTY; } else { // Interpolate // Fraction = (Temp - Target) / Band float fraction = (currentTemp - targetTemp) / CONTROL_BAND; newDuty = MIN_DUTY + (int)(fraction * (MAX_DUTY - MIN_DUTY)); } // Constrain safely if (newDuty > MAX_DUTY) newDuty = MAX_DUTY; if (newDuty < 0) newDuty = 0; // Should not happen with unsigned logic but... // Write PWM OCR1A = newDuty; delay(500); // 2Hz update rate }