10Hz PWM output

I have an application similar to this to drive a 12V, 10Hz PWM Mercedes Benz radiator cooling fan.

I was able to get this to work with a 10Hz PWM output with the Yourdruino RoboRed, which uses the UNO drivers and is equivalent to the Uno except it handles a little more voltage and current.

I used a special PWM.h library which allows frequencies under 31 Hz.

But I am now having problems with the Nano 33 BLE. Apparently when I try to compile with the NANO 33 BLE I get a message stating it cannot compile for this board. I believe the problem is that UNO libraries are not compatible with the NANO because they use a different chip set. So I am looking for alternatives to the PWM.h library. But would prefer to use code to will work with all boards.

Over the next several days or so I will be trying the codes presented in post 4, 10, and 23. I will also try a modified version of the Blink Example code very similar to post 4.

But I have several issues. With the exception of the Blink code I have no understanding of what these codes are doing.

From post #4 @chrisknightley presents this:

I really have no idea what this YxYY constant type formatting means. Also in post 4 and in the blink code, the delay statement is used. I understand why the delay statement is being used but when I tried to use the delay to pause programing to read the serial output all of my variables all the variables started to report nonsense. Hopefully that will go away if I don't use the PWM.h library. Notice all the delay statements had to be commented out in my attached code.

@GolamMostafa code in post 10 and modified as in post 23 uses the same YxYY format constants and also uses what appears to me as variables COM1A1, WGM11, WGM12, WGM13, CS10 and CS11. These variables don't appear to be initialized or assigned any where.

My level of expertise on programing is Fortran, BASIC and VBA

#include <PWM.h>
// Note for low frequency < 31Hz,the high resolution pwmWriteHR() must be used with 16 bit PWM Value
//For the RoboRed only pins 9 and 10 can be used with 16 bit resolution

int RadPin = A0;
int CondPin = A1;
int AuxPin = A2;

int RadVals[6] = {0, 0, 0, 0, 0, 0};
int CondVals[6] = {0, 0, 0, 0, 0, 0};
int AuxVals[6] = {0, 0, 0, 0, 0, 0};

// 10 bit temperature equivolents for radiator sensor
int Rad20 = 444;    //85C
int Rad30 = 484;    //90C
int Rad40 = 524;    //95C
int Rad50 = 562;    //100C
int Rad60 = 599;    //105C
int Rad70 = 635;    //110C
int Rad80 = 668;    //115C
int Rad90 = 699;    //120C

// 10 bit temperature equivolents for condenser sensor
int Cnd20 = 391;    //40C
int Cnd30 = 440;    //45C
int Cnd40 = 489;    //50C
int Cnd50 = 537;    //55C
int Cnd60 = 583;    //60C
int Cnd70 = 627;    //65C
int Cnd80 = 668;    //70C
int Cnd90 = 706;    //75C

int Aux20 = 0;
int Aux30 = 0;
int Aux40 = 0;
int Aux50 = 0;
int Aux60 = 0;
int Aux70 = 0;
int Aux80 = 0;
int Aux90 = 0;

int RadSpeed = 10;
int CondSpeed = 10;
int AuxSpeed = 10;

int FanSpeed = 10;    // Set fan to 10% PWM = off
int FanPin = 9;       // Set Fan pin
int16_t PWMVal;
int32_t frequency = 10;

int t1;

void setup() {
  // put your setup code here, to run once:
  pinMode(RadPin, INPUT);
  Serial.begin(9600);
  InitTimersSafe()

  //sets the frequency for the specified pin
  ;  bool success = SetPinFrequencySafe(FanPin, frequency);

  //if the pin frequency was set successfully, pin 13 turn on. Pin 13 can be used to light an LED etc.
  if (success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
  }

}

void loop() {
  // put your main code here, to run repeatedly:
  //this code prints sensor value to the console
  Serial.print("Rad pin input = "); Serial.println(RadVals[0]);
  Serial.print("Cond pin input = "); Serial.println(CondVals[0]);
  Serial.print("Aux pin input = "); Serial.println(AuxVals[0]);
  Serial.println("Yo1");
  Serial.println();
  // delay(1000);
  // t1=millis();
  // do {
  // } while (millis()-t1 <1000);

  // initialize values
  RadVals[5] = 0;
  CondVals[5] = 0;
  AuxVals[5] = 0;

  //read sensor value and set upper limit cap
  //for (int i = 1; i <= 5; i++)  {

  int i = 0;
  do {
    // Find 5 data points
    RadVals[i] = analogRead(RadPin);
    CondVals[i] = analogRead(CondPin);
    AuxVals[i] = analogRead(AuxPin);

    Serial.print("Rad pin input stored = "); Serial.println(RadVals[i]);
    Serial.print("Cond pin input stored = "); Serial.println(CondVals[i]);
    Serial.print("Aux pin input stored = "); Serial.println(AuxVals[i]);
    Serial.println(i);
    Serial.println("Yo2");
    Serial.println();
    // delay(1000);

    //Serial.print("Rad pin input before sum = "); Serial.println(RadVals[5]);
    //Serial.println("Yo25");
    //Serial.println();

    // sum all 5 data points
    RadVals[5] = RadVals[5] + RadVals[i];
    CondVals[5] = CondVals[5] + CondVals[i];
    AuxVals[5] = AuxVals[5] + AuxVals[i];

    Serial.print("Rad pin input sum = "); Serial.println(RadVals[5]);
    Serial.print("Cond pin input sum = "); Serial.println(CondVals[5]);
    Serial.print("Aux pin input sum = "); Serial.println(AuxVals[5]);
    Serial.println(i);
    Serial.println("Yo3");
    Serial.println();
    // delay(1000);



    i = i + 1;

  } while (i < 5);

  // Find Average of 5 data points
  RadVals[5] = RadVals[5] / 5;
  CondVals[5] = CondVals[5] / 5;
  AuxVals[5] = AuxVals[5] / 5;

  Serial.print("Rad pin input Average = "); Serial.println(RadVals[5]);
  Serial.print("Cond pin input Average = "); Serial.println(CondVals[5]);
  Serial.print("Aux pin input Average = "); Serial.println(AuxVals[5]);
  Serial.println(i);
  Serial.println("Yo4");
  Serial.println();
  // delay(1000);

  if (RadVals[5] < Rad20) RadSpeed = 10;
  if (RadVals[5] < Rad30 and RadVals[5] >= Rad20) RadSpeed = 20;
  if (RadVals[5] < Rad40 and RadVals[5] >= Rad30) RadSpeed = 30;
  if (RadVals[5] < Rad50 and RadVals[5] >= Rad40) RadSpeed = 40;
  if (RadVals[5] < Rad60 and RadVals[5] >= Rad50) RadSpeed = 50;
  if (RadVals[5] < Rad70 and RadVals[5] >= Rad60) RadSpeed = 60;
  if (RadVals[5] < Rad80 and RadVals[5] >= Rad70) RadSpeed = 70;
  if (RadVals[5] < Rad90 and RadVals[5] >= Rad80) RadSpeed = 80;
  if (RadVals[5] >= Rad90) RadSpeed = 90;

  if (CondVals[5] < Cnd20) CondSpeed = 10;
  if (CondVals[5] < Cnd30 and CondVals[5] >= Cnd20) CondSpeed = 20;
  if (CondVals[5] < Cnd40 and CondVals[5] >= Cnd30) CondSpeed = 30;
  if (CondVals[5] < Cnd50 and CondVals[5] >= Cnd40) CondSpeed = 40;
  if (CondVals[5] < Cnd60 and CondVals[5] >= Cnd50) CondSpeed = 50;
  if (CondVals[5] < Cnd70 and CondVals[5] >= Cnd60) CondSpeed = 60;
  if (CondVals[5] < Cnd80 and CondVals[5] >= Cnd70) CondSpeed = 70;
  if (CondVals[5] < Cnd90 and CondVals[5] >= Cnd80) CondSpeed = 80;
  if (CondVals[5] >= Cnd90) CondSpeed = 90;

  if (AuxVals[5] < Aux20) AuxSpeed = 10;
  if (AuxVals[5] < Aux30 and AuxVals[5] >= Aux20) AuxSpeed = 20;
  if (AuxVals[5] < Aux40 and AuxVals[5] >= Aux30) AuxSpeed = 30;
  if (AuxVals[5] < Aux50 and AuxVals[5] >= Aux40) AuxSpeed = 40;
  if (AuxVals[5] < Aux60 and AuxVals[5] >= Aux50) AuxSpeed = 50;
  if (AuxVals[5] < Aux70 and AuxVals[5] >= Aux60) AuxSpeed = 60;
  if (AuxVals[5] < Aux80 and AuxVals[5] >= Aux70) AuxSpeed = 70;
  if (AuxVals[5] < Aux90 and AuxVals[5] >= Aux80) AuxSpeed = 80;
  if (AuxVals[5] >= Aux90) AuxSpeed = 90;

  FanSpeed = max(RadSpeed, CondSpeed);
  //map and assign pwm values to the fan output 0 to 65535 corresponds to 0 to 100%
  PWMVal = map(FanSpeed, 0, 100, 0, 65535);

  Serial.print("Rad pin input Average = "); Serial.println(RadVals[5]);
  Serial.print("Cond pin input Average = "); Serial.println(CondVals[5]);
  Serial.print("Aux pin input Average = "); Serial.println(AuxVals[5]);
  Serial.print("Fan Speed = "); Serial.println(FanSpeed);
  Serial.print("PMWval = "); Serial.println(PWMVal);
  Serial.println("Yo5");
  Serial.println();
  // delay(1000);


  //write the PWM value to the pwm output pin
  pwmWriteHR(FanPin, PWMVal);



}

10Hz is 100mS, so you could easily do PWM with software. One way to do this in a cross-platform compatible manner is by adapting the https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay example to schedule changing future events:

/*
  Blink PWM without Delay

  Turns on and off a light emitting diode (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  The circuit:
  - Use the onboard LED.
  - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
    and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
    is set to the correct LED pin independent of which board is used.
    If you want to know what pin the on-board LED is connected to on your
    Arduino model, check the Technical Specs of your board at:
    https://www.arduino.cc/en/Main/Products

  created 2005
  by David A. Mellis
  modified 8 Feb 2010
  by Paul Stoffregen
  modified 11 Nov 2013
  by Scott Fitzgerald
  modified 9 Jan 2017
  by Arturo Guadalupi
  modified by 2022-02-07 for 10Hz PWM w/o delay per 
  https://forum.arduino.cc/t/setting-pwm-frequency-5hz/908715/25?u=davex

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
modified to 
https://forum.arduino.cc/t/setting-pwm-frequency-5hz/908715/26?u=davex
*/

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long nextMillis = 0;        // will store next time LED will be updated

// PWM period in ms:
const long period = 100;           // interval at which to cycle (milliseconds)
int duty_cycle = 25 ; // percent;
long interval =  0; 

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if ((signed long)(currentMillis - nextMillis) >= 0) {

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
      interval = duty_cycle * period/100;
    } else {
      ledState = LOW;
      interval = period - (duty_cycle * period/100);
    }
    // schedule the next time to blink the LED
    nextMillis += interval;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

Thanks for the reply.

Looks like it will do the job. However I do have to set my duty cycle in the main loop, but I don't see it effecting anything.

Will give it a try.

Be aware @mb107 is very resistant to advice and is, in my experience, unwilling to cooperate.

See
https://forum.arduino.cc/t/understanding-analogread/955401/44

Be aware I asked @Grumpy_Mike a very simple question about understanding AnalogRead() which has a very simple answer.

Unfortunately Grumpy_Mike insisted on seeing the circuitry and code and nit picking the code and circuitry which he absolutely does not understand and told me how it violates Physics etc.

Well here is the fan running perfectly with the circuited and code that violates all laws of physics.

Grumpy_Mike is an appropriate name.

Yes be aware of Grumpy_Mike

Seems reasonable to me.

Yes it is. And when I showed it to him and he couldn't understand either the circuit or the code he started asking for phantom circuits that don't exist. All this after the solution to the original question had been established. Focus: The question us understanding AnalogRead. It was not "Do you like my circuit and code".

Were you able to explain it cogently? That's what engineers respond best to.

Back on topic, I'm still trying to understand why you would need sub microsecond accuracy on a 5Hz signal...

1 Like

I had to look that one up. So did I explain it to him cogently? I don't know, read the thread and decide for yourself. But also ask yourself, did he explain anything to me cogently. I'm an engineer as well, a mechanical engineer.

Also find me the post where I did not cooperate with him. I did not answer his last request because I had my answer all along at post 31 and could not figure out what he was asking, probably because he didn't explain it cogently.

And I did not bad mouth him until he bad mouthed me.

I don't really care about that. Let's just get back to the actual topic. It would be great if you could answer my question...

In fact, it is implicit in the first question that was asked, reply #2.

I told you how to achieve 4 microsecond accuracy in reply #18. I also see upon reading the entire thread that many people have suggested the same or similar approach.

But, keep in mind the actual frequency accuracy of the CPU clock crystal circuit...

1 Like

I'm running at 10Hz. And I don't believe I need sub microsecond accuracy. Reliability is the most important to me. It looks to me the solutions proposed in post 4 and by DaveX in post 26 are both non-sub microsecond. I believe they will work fine so I will give them a try. But I would not mind trying to figure out what is going on in post 10 even if it's just for educational purposes.

Ultimately the procedure I should use would be the one most likely to not fail when the fan is supposed to be off in the Antarctic winter and needs to running at 90% in the Arizona summer.

Then you don't need to use any hardware timer. Your other comments seem to pertain more to the hardware implementation, which hasn't been discussed so far.

1 Like

Sounds good.

Are these the hardware timers. They are use in the code in post 4 and 10. I don't see them in post 26.

Yes, those are mnemonic names for timer control registers in the MCU.

1 Like

Thanks

I assume that is I were to use something like that, they might be different for the UNO vs. Nano 33 BLE?

Yes, different processors have different internal peripherals, or similar ones with different interfaces. A comparison of data sheets will make that clear.

1 Like

Thank You

Probably explains why the PWM.h library doesn't work with the Nano33.

Give me a day or so to try some of this out.

The circuit you showed in that thread had a voltage that you claim was generated by simply adding a pull down resistor to an analogue input pin, with no pull up resistor. That is simply impossible. By your own admission you are not an electronic engineer, I am.

A mechanical equivalent of the circuit you posted is if I posted a mechanical design for a picture hanger and had a part simply described as a" sky hook".

You also violated the forum rules by hijacking a thread that was 4 months old. This is known as a necro thread.

You may want to read this before you proceed:-
how to get the best out of this forum

You should realize that I have no idea what pull up or pull down means. If you don't have the patience to deal with that you should not be involved with educational forums.

You seem to be the only one concerned about my necro thread. The people who asked the original question have gotten there answers and have moved on. I applied to this thread because it was relevant to anyone searching for low frequency PWM. It also add value to this thread in that programing for the UNO and Nano require different code If it was against the forum rules then the forum shouldn't encourage me to look at other threads when I post a question. And the statement below that says "This topic will close 6 months after the last reply" should be changed to a lower value.