PWM with analogwrite()

Newbie going bonkers!
Hi all - I am a machinist building a tensile testing machine using Arduino MEGA2560, and I want the fan to be controlled by the cabinet temperature.
This worked perfectly well initially, but not after making a few changes to save settings in EEPROM.

The first call in FanControl::update() to analogWrite(fanPin, 255) still works fine.
But the second call to analogWrite(fanPin, int(fanSpeed)) does not work.
Even if I replace analogWrite(fanPin, int(fanSpeed)) with analogWrite(fanPin, 255) nothing happens.

//v19
#include "FanControl.h"
#include <EEPROM.h>
#include <DallasTemperature.h>

// EEPROM addresses for the calibration offsets and temperature bounds
#define FAN_CALIBRATION_OFFSET_ADDR 12
#define FAN_MIN_TEMP_ADDR 16
#define FAN_MAX_TEMP_ADDR 20

FanControl::FanControl(int oneWireBus, int fanPin, float calibrationOffset, int minTemp, int maxTemp): 
    oneWire(oneWireBus), sensors(&oneWire), fanPin(fanPin), fanStartMode(true), hasPrinted(false), lastUpdateTime(0), startModeTime(0) {
    // Read the stored values from EEPROM upon initialization
    EEPROM.get(FAN_CALIBRATION_OFFSET_ADDR, this->calibrationOffset);
    EEPROM.get(FAN_MIN_TEMP_ADDR, this->fanMinTemp);
    EEPROM.get(FAN_MAX_TEMP_ADDR, this->fanMaxTemp);
}

void FanControl::initialize() {
    sensors.begin();                  // Start communication with the DS18B20 sensor
    pinMode(fanPin, OUTPUT);          // Set the fan pin as output
    startModeTime = millis() + 3000;  // Delay fan start by 3 seconds
    fanStartMode = true;              // Ensure that start mode is activated
}

void FanControl::update() {
    unsigned long currentMillis = millis();
    sensors.requestTemperatures();
    float rawTemperature = sensors.getTempCByIndex(0);
    if (rawTemperature == DEVICE_DISCONNECTED_C) {
        Serial.println("Error: Sensor disconnected!");
        return;
    }
    // Check if in delayed start period
    if (currentMillis < startModeTime) {
        return;                                               // In delayed start period, do nothing (return=exit function)
    }
    if (fanStartMode && (currentMillis - startModeTime < 2000)) {
        analogWrite(fanPin, 255);                             // Run the fan at maximum speed
        return;
    } else if (fanStartMode) {
        fanStartMode = false;                                 // Deactivate start mode, move to temperature control
    }
    if (!fanStartMode && (currentMillis - lastUpdateTime >= 8000)) {      // Update every 3 seconds
        lastUpdateTime = currentMillis;
        sensors.requestTemperatures();
        float rawTemperature = sensors.getTempCByIndex(0);                // Read raw temperature
        //float rawTemperature = 23.19; // Just for testing
        float correctedTemperature = rawTemperature + calibrationOffset;  // Apply calibration offset
        // Calculate fan speed based on temperaature
        Serial.println("========================");
        Serial.print("Cabinet temperature: ");Serial.println(correctedTemperature);
        Serial.println(fanMinTemp);
        Serial.println(fanMaxTemp);
        int fanSpeed=0;
        if (correctedTemperature < fanMinTemp) {
            fanSpeed = 0;                                                 //Stop if temperature is below minTemp
        } else if (correctedTemperature > fanMinTemp && correctedTemperature < fanMaxTemp) {
                                                                          // Linear interpolation between fanMinTemp and fanMaxTemp
            Serial.println("1");
            fanSpeed = map(correctedTemperature, fanMinTemp, fanMaxTemp, 90, 255);
        } else if (correctedTemperature >= fanMaxTemp) {
            fanSpeed = 255;                                               //Max speed if temperature is above maxTemp
        }
        Serial.print("fanSpeed : ");Serial.println(fanSpeed);
        analogWrite(fanPin, int(fanSpeed));
    }
}

void FanControl::checkSerial(String command) {
    Serial.println("Received in FanControl: " + command); 
    if (command.startsWith("calOffset:")) {
        float offset = command.substring(10).toFloat();
        saveCalibrationOffset(offset);
        Serial.print("***** New FanControl calibration offset set to: ");
        Serial.println(offset, 2);
    } else if (command.startsWith("minTemp:")) {
        int minTemp = command.substring(8).toInt();
        saveTemperatureBounds(minTemp, this->fanMaxTemp);
        Serial.print("****** New FanControl minTemp set to: ");
        Serial.println(minTemp,1);
    } else if (command.startsWith("maxTemp:")) {
        int maxTemp = command.substring(8).toInt();
        saveTemperatureBounds(this->fanMinTemp, maxTemp);
        Serial.print("****** New FanControl maxTemp set to: ");
        Serial.println(maxTemp,1);
    }
}

void FanControl::saveCalibrationOffset(float offset) {                  //Write offset to EEPROM
    EEPROM.put(FAN_CALIBRATION_OFFSET_ADDR, offset);
    this->calibrationOffset = offset;
}

void FanControl::saveTemperatureBounds(int minTemp, int maxTemp) {      //Write bounds to EEPROM  
    EEPROM.put(FAN_MIN_TEMP_ADDR, minTemp);
    EEPROM.put(FAN_MAX_TEMP_ADDR, maxTemp);
    this->fanMinTemp = minTemp;
    this->fanMaxTemp = maxTemp;
}; 

In serial monitor I get

========================
Cabinet temperature: 23.30
22
35
1
fanSpeed : 102

-- which seems fine.
What am I missing?

1 Like

Your code doesn't contains setup() and loop() functions. Please show the main sketch for this code.

1 Like

you probably need to move the EEPROM.get into your initialize() function


how does this update every 3s ??

1 Like

This is the .ino code:

#include "Climate.h"
#include "EndStop.h"
#include "Input.h"
#include "FanControl.h"
#include "LoadCell.h"
#include "MotorControl.h"
#include "TimerISR.h" 
#include "EEPROM.h"

// Define pin numbers and other constants
const int ONE_WIRE_BUS = A1;
const int FAN_PIN = 3;
const int LOADCELL_DOUT_PIN = 39;
const int LOADCELL_CLK_PIN = 38;

// Create objects
FanControl fanControl(ONE_WIRE_BUS, FAN_PIN);
LoadCell loadCell(LOADCELL_DOUT_PIN, LOADCELL_CLK_PIN);
Climate climate;
EndStop hallSensor(A0);
Input inputHandler;  

void setup() {
    Serial.begin(9600);
    Serial.println(); Serial.println("-------"); Serial.println("UT-5000"); Serial.println("-------");
    initializeTimers(); // Initialiseing Timer2 og Timer3 as defined i TimerISR
    fanControl.initialize();
    climate.InitializeSensor();
    hallSensor.InitializeSensor();
    inputHandler.Initialize();
    loadCell.begin(); 

    // Read from EEPROM
    float tempOffset, humidityOffset, pressureOffset, calOffset;
    int minTemp, maxTemp;
    EEPROM.get(0, tempOffset);
    EEPROM.get(4, humidityOffset);
    EEPROM.get(8, pressureOffset);
    EEPROM.get(12, calOffset);
    EEPROM.get(16, minTemp);
    EEPROM.get(20, maxTemp);

    // Udskriver kalibreringsdata
    Serial.println("Calibration data from EEPROM:");
    Serial.print("setTempOffset: "); Serial.print(tempOffset, 1); Serial.println("  (Ambient Temperature Offset)");
    Serial.print("humidityOffset: "); Serial.print(humidityOffset, 1); Serial.println("  (Ambient Humidity Offset)");
    Serial.print("pressureOffset: "); Serial.print(pressureOffset, 1); Serial.println("  (Ambient Pressure Offset)");
    Serial.print("calOffset: "); Serial.print(calOffset, 1); Serial.println("  (Cabinet Temperature Offset)");
    Serial.print("minTemp: "); Serial.print(minTemp, 1); Serial.println("  (Fan starting temperature)");
    Serial.print("maxTemp: "); Serial.print(maxTemp, 1); Serial.println("  (Temperature for max. speed)");
}

void loop() {
    // Behandler serielle kommandoer
    if (Serial.available()) {
        String command = Serial.readStringUntil('\n');
        command.trim();
        if (command.startsWith("calOffset:") || command.startsWith("minTemp:") || command.startsWith("maxTemp:")) {
            fanControl.checkSerial(command);
        } else if (command.startsWith("setTempOffset:") || command.startsWith("setHumidityOffset:") || command.startsWith("setPressureOffset:")) {
            climate.checkSerial(command);
        } else {
            Serial.println("Command not recognized.");
        }
    }

    // Opdaterer sensorer og inputhåndtering
  //  climate.updateReading();
  //  inputHandler.CheckButtons();
  //  inputHandler.CheckEmergencyStop();
    fanControl.update(); 
  //  hallSensor.processReading();
}

It doesn't - you can ignore comments.

Reading from EEPROM works as intended, according to serial print, right?

I meant the one in the FanControl constructor

Calling the constructor happens super early in the process and the Arduino main() has not been called yet. I'm unsure if everything is set-up correctly for the EEPROM library to work.

Sorry if I am being a bit thick here...
fanMinTemp and fanMaxTemp are correctly read as 22 and 35, and fanSpeed is calculated to be 102 or around 4.8V.
Does that not rule out EEPROM issues?
Why will even analogWrite(fanPin, 255) not work?

no pb. I'm saying it might or might not work depending on which arduino and EEPROM library you are using. For example on an ESP8266 you need to call EEPROM.begin() to activate the EEPROM emulation and so this would not work in the constructor. On an AVR this might work - and it seems to be the case for you.


it should

1 Like

ah I see this in your code

on a MEGA Timer2 is used for PWM on pins 9 and 10 and Timer3 is used for PWM on pins 2, 3, and 5.
So if you are using those timers, you loose pwm on the associated pins.

1 Like

Thanks a lot, you saved my day!

glad it helped