Pwm fan control with rotary encoder

Hi, I am trying to control 2 PWM PC fans with my Arduino Leonardo "pro micro" and a rotary encoder. I asked Chatgpt since I didn't want to code everything. It works great I can select whit fan I want to change by clicking the button and then rotate the encoder to increase or decrease the speed, but when I rotate, it detects to click if I do half-click it detect one click, which it borders me a lot, but it won't matter that much I just wonder if someone can help me.

const int encoderPin1 = 2; // CLK
const int encoderPin2 = 4; // DT
const int encoderButtonPin = 6; // SW

// Pins for fan control
const int fan1Pin = 3; // Fan 1 PWM control pin
const int fan2Pin = 5; // Fan 2 PWM control pin

// Variables to store fan speeds
int fanSpeed1 = 127;
int fanSpeed2 = 127;

// Variables to store previous positions
int prevPosition1 = 0;
int prevPosition2 = 0;

// Variable to store last time position changed
unsigned long lastChangeTime = 0;

// Variable to track active fan (1 = fan1, 2 = fan2)
int activeFan = 1;

void setup() {
  // Initialize the serial communication
  Serial.begin(9600);

  // Set encoder pin modes
  pinMode(encoderPin1, INPUT_PULLUP);
  pinMode(encoderPin2, INPUT_PULLUP);
  pinMode(encoderButtonPin, INPUT_PULLUP);

  // Set fan control pins as output
  pinMode(fan1Pin, OUTPUT);
  pinMode(fan2Pin, OUTPUT);
}

// Read the rotary encoder position
int readEncoder1(int pinA, int pinB, int prevPosition1) {
  static int position1 = 10;
  static int pinALast = LOW;
  static int aVal = LOW;

  aVal = digitalRead(pinA);

  if (aVal != pinALast) {
    if (digitalRead(pinB) != aVal) {
      position1++;
    } else {
      position1--;
    }
  }

  pinALast = aVal;
  position1 = constrain(position1, 0, 25);

  return position1;
}

int readEncoder2(int pinA, int pinB, int prevPosition2) {
  static int position2 = 10;
  static int pinALast = LOW;
  static int aVal = LOW;

  aVal = digitalRead(pinA);

  if (aVal != pinALast) {
    if (digitalRead(pinB) != aVal) {
      position2++;
    } else {
      position2--;
    }
  }

  pinALast = aVal;
  position2 = constrain(position2, 0, 25);

  return position2;
}


void loop() {
  // Read the rotary encoder
  int position1 = prevPosition1;
  int position2 = prevPosition2;

  if (activeFan == 1) {
    position1 = readEncoder1(encoderPin1, encoderPin2, prevPosition1);
  } else if (activeFan == 2) {
    position2 = readEncoder2(encoderPin1, encoderPin2, prevPosition2);
  }

  // Check if the position has changed for fan 1
  if (position1 != prevPosition1) {
    // Check if enough time has elapsed since last change
    if (millis() - lastChangeTime > 125) {
      // Update the previous position and last change time
      prevPosition1 = position1;
      lastChangeTime = millis();

      // Adjust fan speed based on encoder position
      fanSpeed1 = map(position1, 0, 25, 0, 255);

      // Update the fan speed
      analogWrite(fan1Pin, fanSpeed1);

      // Print the current fan speed
      Serial.print("Fan 1 Speed: ");
      Serial.println(fanSpeed1);
    }
  }

  // Check if the position has changed for fan 2
  if (position2 != prevPosition2) {
    // Check if enough time has elapsed since last change
    if (millis() - lastChangeTime > 125) {
      // Update the previous position and last change time
      prevPosition2 = position2;
      lastChangeTime = millis();

      // Adjust fan speed based on encoder position
      fanSpeed2 = map(position2, 0, 25, 0, 255);

      // Update the fan speed
      analogWrite(fan2Pin, fanSpeed2);

      // Print the current fan speed
      Serial.print("Fan 2 Speed: ");
      Serial.println(fanSpeed2);
    }
  }

  // Check if the encoder button is pressed
  // Check if the encoder button is pressed
  if (digitalRead(encoderButtonPin) == LOW) {
    delay(250); // Delay for button debouncing

    // Toggle the active fan
    if (activeFan == 1) {
      activeFan = 2;
      Serial.println("Active Fan: Fan 2");
    } else {
      activeFan = 1;
      Serial.println("Active Fan: Fan 1");
    }
  }
}

`

Did chatGPT make sense of the modification you wanted?

What sort of encoder are you using? Does it have 1 detent per cycles?

This code is changing state at both the rising and falling edge of the A-phase of the quadrature signal, which, if I'm understanding your words correctly, is twice as fast as the detents/clicks in your knob.

An easy fix would be to double the 0-25 ranges to 0-25 in lines like:

fanSpeed1 = map(position1, 0, 25, 0, 255);

or

position2 = constrain(position2, 0, 25);

Alternately, you could adjust the counting to trigger on just a FALLING or RISING edge of the one of the quadrature phases with lines something like:

  if (aVal != pinALast && aVal == LOW) {
    if (digitalRead(pinB) != aVal) {
      position2++;
    } else {
      position2--;
    }
  }
2 Likes

My standard advice:

Keep asking the robot until he/she/it gives you the correct result you expect.

One answer could probably be 42.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.