Need a low frequency (can preset at each value of 15Hz to 25Hz) PWM signal

Hi,

I am replacing the PWM signal input to my treadmill. It is to the motor controller board that then drives the 2.5HP motor. . I know the controller board needs a 5v (@ Hi) input signal frequency of somewhere between 15 and 25Hz (I need to preset to each frequency value and, by trial and error, find the exact frequency that triggers the motor movement. The duty cycle will be in the range of 15% to 85% for the motor speed range.

I am very new to Arduino coding. I have successfully put together a potentiometer analog input and pin 9 output that has a PWM output that gives the 15% to 85% duty cycle range using the mapping of input range 0-1023 to output 0-255. It is successful in adjusting an LED brightness (but not to get the motor to work).

The problem is that the lowest frequency I have been able to set the Arduino circuit is 30.5Hz by using the code line:
TCCR1B = TCCR1B & B11111000 | B00000101

The motor control board does not work at this frequency and is known to be very sensitive to the input frequency of the PWM (known to be somewhere in the range 15Hz-25Hz depending on the board).

I'm seeking help with a source for the Arduino code needed to specifically have a lower PWM frequency, where I can put in a variable in the code that allows me to preset to an exact frequency (and still vary the duty cycle by potentiometer adjustment). I have read a lot of material but have found it baffling with quite a lot of tables and considerations - sometimes with example code that, when I try it, does not quite do the job. Any helpers on a solution to this?

I know I can build a dual ic555 timer circuit to do the same job (with fine tune variable resistors to set the frequency) but this feels like giving-in and would prefer to use Arduino with its coded control.

15Hz-25Hz depending on the board

That is a very low frequency. You could do that easily in the loop just using millis() instead of using a built in timer.
You are planning to use an opto coupler or some other means of isolation from the motor control board ?

Indeed, just use millis() timing. Look at Blink Without Delay, modify that. Unless the device has to do a lot more work, this will do just fine.

How do you plan to do a 555 circuit? They normally can't provide duty cycles of >50%.

Thanks for early feedback.

A loop command may work but I've been concerned that just using the delay function would mess up the duty cycle percentage which is crucial to motor speed setting. I've not seen the BlinkWithoutDelay function before so will look into that - thanks.

The ic555 solution, is my last-resort fallback option if I can't get the Arduino circuit to work. It consists of two ic555s in series, the first sets the frequency, the second the duty cycle. More details on this (and links to others) that people have posted are on MC-2100 Treadmill Motor speed control circuit

The motor controller board comes with a opto-coupler for isolation so should be protected (to some degree) from my breadboard testing.

If anyone has a "set very low PWM frequency" known working code that still allows for a wide duty cycle variation within it, please let me know.

Andrew_S_C:
If anyone has a "set very low PWM frequency" known working code that still allows for a wide duty cycle variation within it, please let me know.

Blink Without Delay (and even good old Blink) does exactly that. Very low PWM frequency (by default 0.5 or 1 Hz), 50% duty cycle. Changing the on and off times gives you any duty cycle you need.

A few comments on that dual 555 schematic:

  • instead of two 555s you can also use a 556.
  • C1 and C2 should be film capacitors, much better stability when it comes to timing. This person used an electrolytic for C1 and 3x ceramic for C2. Especially ceramics give quite unstable results.
  • C2 can be replaced by a single 330 nF cap. No need to have three in parallel. That's probably because of what he happened to have on hand (just like that 2.2µ electrolytic).
  • that 0.68µ cap can indeed be necessary; a bigger value will work fine as well. It's for power supply decoupling. Better would be to use one larger electrolytic and a smaller ceramic in parallel (e.g. 10µF and 100 nF).

I recently worked on an application that required a 20Hz PWM. Indeed the lowest possible frequency on arduino PWMs is 30.5 Hz, so I did it in loop using millis.
Then the application got more complicated and to avoid all concerns about loosing the correct timing I made the main application running on one arduino and I have a second board running just the timing code. the two are easily connected via I2C so from the first arduino I can set both frequency and duty cycle at run time.
hope this helps

You can use timer interrupts as well for this. Those work for much longer intervals, and are not affected by other tasks. I've used it for blinking an led during a long running, otherwise blocking task. No need for a second Arduino! Just set the interrupt to fire and set the output accordingly, together with the length of the next interval (for duty cycle changes).

Thanks again for all of the feedback so far.

I must be missing something. I have investigated the Blink Without Delay function and some example circuits/code. They do produce a low frequency but they have the duty cycle locked at 50%. The only example I saw where the duty cycle could be changed actually then changed the frequency as well.

For this project I need a signal that has a fixed duration low frequency (unchanging so the motor controller recognises the signal) whilst also being able to change the duty cycle (over a 15% to 85% range to change the motor speed) within that fixed frequency. So far, I'm not seeing Blink Without Delay as the solution for this need. But I may have not spotted how to set up the code to do this.

If anyone can point me to Blink Without Delay example code that a) has a fixed low frequency and b) allows the duty cycle to be varied as above (say, with a potentiometer) without changing the frequency, that would be very helpful.

Of course, an unmodified blink without delay will not fulfill all your requirements, but it is very close:

Lets say you want a frequency of 20Hz.
If you want a 10% duty cycle, that is 5ms ON and 45mS OFF
If you want a 20% duty cycle, that is 10ms ON and 40mS OFF
etc.
etc.

Therefore:
The ON period = ( 10 * DC / Freq ) milliseconds, where DC is duty cycle in % and Frequency in Herz
The OFF period = ( 10 * (100 - DC ) / Freq ) milliseconds.

So all you want is the blink sketch with variable on and off times.

Do you want to be able to vary the frequency, and if so, within what range ?
You've already said the duty cycle is from 15% to 85 %

How do you want to enter these parameters into the sketch, say with a potentiometer ?
How do you want to display the selected parameters (if at all) ? with a 1602 LCD ?

Exactly, like that. Just calculate the on/off periods. The same if you would use timer interrupts, then the moment the interrupt is called set the pin and reset the timer to reflect onTime or offTime as appropriate.

unsigned int period = 500; // 500 ms period.
float dutyCycle; // 20% duty cycle.
unsigned long lastChange = 0;
bool signal = LOW;

void loop() {
  dutyCycle = map(analogRead(potPin), 0, 1023, 1500, 8500) / 10000.0; // returns 0.15-0.85 based on the analog reading 0-1023.
  unsigned int onTime = period * dutyCycle;
  unsigned int offTime = period * (1 - dutyCycle);
  if (signal == HIGH) {
    if (millis() - lastChange > onTime) {
      signal = LOW;
      lastChange += onTime;
    }
  else {
    if (millis() - lastChange > offTime) {
      signal = HIGH;
      lastChange += offTime;
    }
  }
  digitalWrite(outPin, signal);
}

This should do. Not tested or even compiled.

Thanks again for all of the help. Looks to me to be getting close to a solution to my problem.

To summarise my understanding on the recent feedback…

Have a variable ‘period’ that is set to the desired cycle time (i.e. 1 / frequency). So, ‘period’ set to 50mS would give a 20Hz frequency.
Have a variable ‘dutycycle’ that takes an analog (say potentiometer) reading expressed as a range value of 0-1023 and maps these limits to 15% and 85% duty cycle min and max values
Have variables ‘onTime’ and ‘offTime’ and calculate the values of these needed usng the duty cycle and period values.
Use the loop with If statements to then move between the High and Low output signal status based on the previous signal status and the time. The increment the time with a variable ‘lastChange’

The challenges I still see for me to get to a working solution are…

  1. How does the potentiometer input circuit (to adjust the duty cycle between 15% and 85%) get recognised and fit in with this example code? I’m looking to set the motor at any speed in the range
  2. The very useful example code from wvmarle does not compile. It has the error “ 'dutyCycle' was not declared in this scope”. From my reading of the code, ‘duty cycle’ was declared as a float variable – but this is where my weak knowledge of Arduino code is exposed. What is wrong?
  3. I’m assuming I also need to declare the Arduino pins for input and output. For example:
    const int potPin = A0; // Analog input pin that the potentiometer attached to
    const int outPin = 9; // PWM output pin that the Motor or LED is attached to
    Or have I got this wrong?

On the questions raised by 6v6gt…
How to get this into a sketch and use of a potentiometer. Plan to use a pot maybe with pad switches for speed presets (if I’m feeling confident!). I had originally envisaged a 10K potentiometer input into pin A0. My remaining sketch challenges above probably set out my other needs re the sketch.

On using an LCD screen, I actually don’t need to show the input treadmill speed (and with it time and distance) of the treadmill as I have already built a box with a 1602 LCD display that takes reed sensor feedback on treadmill motor rotations to generate the readings. So once past the use of Arduino serial readout in trialing it, and once the motor turns I can do final calibration. Hope that makes sense.

Thanks again for all of the support.

That's the map() function taking care of. Every time loop() runs, it is calculated.

No surprise it doesn't compile. Did you notice setup() is missing as well? There is more missing such as pin declarations indeed (which you can better declare as const byte rather than const int - more efficient). I didn't try to compile it, it's meant to get you started. You have to fill in the rest of the details (and maybe fix some typos in the process).

A pot can be connected easily, and will do great. One end to GND, the other end to Vcc, wiper to A0. That's it.

Thanks. I had added setup and pin declarations: A0 input from the pot which, same as per my previous design, has the pot wired as you describe) and; pin 13 for the PWM output. It did not successfully compile. It did not seem to like the declaration of 'duty cycle'. I'll take another look.

Please post your current sketch and we'll have a look at what's going wrong.

Thanks for looking at this. I have now gone to the other extreme - defined every variable possible so it does compile but does not produce the PWM output signal. Also think there is at least one } too many though only compiles with all of these in it. Anything you spot would be welcome...

sketch code:
//Purpose: To produce a PWM signal output at 20Hz frequency and a duty cycle that has a minm of 15% and max'm of 85%, the pot adjusted for any value in bewtween
// Arduino UNO board design: Has an input pot resistance value from pot wiper into pin A0 (pot has VCC from 5v pin and Ov from Grd pin). PWM signal ouput defined on pin 9
// Testing: currently using oscillosocpe on output pin 9, unloaded.
// Previous code worked at 30.5Hz, successfully varying LED brightness but frequency was too high for motor controller to work and turn the motor.
// STATUS: DOES COMPILE SUCCESSFULLY BUT NO PWM OUTPUT SIGNAL NOW PRODUCED ON PIN 9.

const int potPin = A0; // Analog input pin that has the potentiometer wiper resistance value
const int outPin = 9; // PWM output pin that Motor Controller) (or LED in testing) is attached to

unsigned long dutyCycle; // Defines the duty cycle, the % of the period (the cycle time) the PWM pulse is high
unsigned long period; // Defines the cycle time ( 1 / frequency) for PWM output
unsigned long lastChange; // The variable for incrementing the time through the loop

unsigned long signal; // The variable holding the value of Hi or Lo output that is derived through the loop

void setup() {

unsigned int period = 50; // 50 ms period, to be used to set the output signal at 20Hz frequency
unsigned long lastChange = 0; // Not sure why need lastchange defined in code above with this here - but does not compile without it!
float dutyCycle; // 20% duty cycle. // Again, Not sure why need dutyCycle defined in code above with this here - but does not compile without it!
bool signal = LOW;
}
void loop() {

dutyCycle = map(analogRead(potPin), 0, 1023, 1500, 8500) / 10000.0; // returns 15% to 85% duty cycle range based on pot rotation reading 0-1023.
unsigned int onTime = period * dutyCycle; // defines the high duration in mS
unsigned int offTime = period * (1 - dutyCycle); // defines the low duration in mS

if (signal == HIGH) {
if (millis() - lastChange > onTime) {
signal = LOW;
lastChange += onTime;
}
else {
if (millis() - lastChange > offTime) {
signal = HIGH;
lastChange += offTime;
}
}
digitalWrite(outPin, signal);
}
}

Thanks again for reviewing this.

This compiles just fine for me:

// Purpose: To produce a PWM signal output at 20Hz frequency and a duty cycle that has a minm of 15 % and max'm of 85%, the pot adjusted for any value in bewtween
// Arduino UNO board design: Has an input pot resistance value from pot wiper into pin A0 (pot has VCC from 5v pin and Ov from Grd pin). PWM signal ouput defined on pin 9
// Testing: currently using oscillosocpe on output pin 9, unloaded.
// Previous code worked at 30.5Hz, successfully varying LED brightness but frequency was too high for motor controller to work and turn the motor.
// STATUS: DOES COMPILE SUCCESSFULLY BUT NO PWM OUTPUT SIGNAL NOW PRODUCED ON PIN 9.

const byte potPin = A0;   // Analog input pin that has the potentiometer wiper resistance value
const byte outPin = 9;    // PWM output pin that Motor Controller) (or LED in testing) is attached to

float dutyCycle;          // Defines the duty cycle, the % of the period (the cycle time) the PWM pulse is high
unsigned long period;     // Defines the cycle time ( 1 / frequency) for PWM output
unsigned long lastChange; // The variable for incrementing the time through the loop
byte outState = LOW;      // The variable holding the value of Hi or Lo output that is derived through the loop

void setup() {
  period = 50000;         // 50,000 us period, to be used to set the output signal at 20Hz frequency.
}
void loop() {

  dutyCycle = map(analogRead(potPin), 0, 1023, 1500, 8500) / 10000.0; // returns 15% to 85% duty cycle range based on pot rotation reading 0-1023.
  unsigned long onTime = period * dutyCycle;                          // defines the high duration in us.
  unsigned long offTime = period * (1 - dutyCycle);                   // defines the low duration in us.

  if (outState == HIGH) {
    if (micros() - lastChange > onTime) {
      outState = LOW;
      lastChange += onTime;
    }
    else {
      if (micros() - lastChange > offTime) {
        outState = HIGH;
        lastChange += offTime;
      }
    }
    digitalWrite(outPin, outState);
  }
}

Fixed various types in the process, and changed millis() to micros() for better resolution of your duty cycle. You have only 50 ms, so a 1 ms resolution is a bit crude. I also changed the name of signal to something that's not red coloured (I've bad experience with such red coloured names so simply avoid them).

Thanks for going through the code and making the mods.

Alas, I've loaded it up and it has the same problem as the version I last added to the forum - it does compile fine BUT there is absolutely no signal output on pin 9 (as per my previous forum entry and in the sketch headlines on STATUS).

To check that my Arduino circuit wiring has not gone awry, I re-loaded the code I wrote for 30.5Hz. It worked fine - oscilloscope shows ~30Hz signal and able to change the duty cycle in range 15% to 85% by adjusting the pot. LED changes brightness as I change the pot.

So, this new version with a lower frequency may compile but there is still something not right with the code. I've looked through your latest version and can't see an obvious error. I can't now determine whether its a simple line error in the text or the code structure itself isn't suitable.

If you have any ideas on where the error lies, that would be most helpful.

Of course, your if blocks are wrong, specifically the else part is linked to the wrong block. That's why you have indentation (and yours was really messed up - that was the first routine fix, ctrl-T in the IDE), makes those things easier to track!
Now you mentioned it I saw it almost instantly (largely thanks to that indentation), didn't look for any other issues when fixing the type declarations.

Small difference - big effects :slight_smile:

// Purpose: To produce a PWM signal output at 20Hz frequency and a duty cycle that has a minm of 15 % and max'm of 85%, the pot adjusted for any value in bewtween
// Arduino UNO board design: Has an input pot resistance value from pot wiper into pin A0 (pot has VCC from 5v pin and Ov from Grd pin). PWM signal ouput defined on pin 9
// Testing: currently using oscillosocpe on output pin 9, unloaded.
// Previous code worked at 30.5Hz, successfully varying LED brightness but frequency was too high for motor controller to work and turn the motor.
// STATUS: DOES COMPILE SUCCESSFULLY BUT NO PWM OUTPUT SIGNAL NOW PRODUCED ON PIN 9.

const byte potPin = A0;   // Analog input pin that has the potentiometer wiper resistance value
const byte outPin = 9;    // PWM output pin that Motor Controller) (or LED in testing) is attached to

float dutyCycle;          // Defines the duty cycle, the % of the period (the cycle time) the PWM pulse is high
unsigned long period;     // Defines the cycle time ( 1 / frequency) for PWM output
unsigned long lastChange; // The variable for incrementing the time through the loop
byte outState = LOW;      // The variable holding the value of Hi or Lo output that is derived through the loop

void setup() {
  period = 50000;         // 50,000 us period, to be used to set the output signal at 20Hz frequency.
}
void loop() {

  dutyCycle = map(analogRead(potPin), 0, 1023, 1500, 8500) / 10000.0; // returns 15% to 85% duty cycle range based on pot rotation reading 0-1023.
  unsigned long onTime = period * dutyCycle;                          // defines the high duration in us.
  unsigned long offTime = period * (1 - dutyCycle);                   // defines the low duration in us.

  if (outState == HIGH) {
    if (micros() - lastChange > onTime) {
      outState = LOW;
      lastChange += onTime;
    }
  }
  else {
    if (micros() - lastChange > offTime) {
      outState = HIGH;
      lastChange += offTime;
    }
  }
  digitalWrite(outPin, outState);
}

Hi, a big thank you. Your error-spot looks to largely have fixed the fundamentals. It compiles and produces a PWM signal whose duty cycle changes as the pot is adjusted.

Alas, it's not without its issues. Using oscilloscope readings...
The voltage output (at hi state) is about 60% of that of the 30Hz design (and any other pin 9 design I've created). Not clear why.
Also the low state output signal has sine wave noise within it (about 2 cycles within each down state) - at about 20% of the hi state voltage (rather than a clean constant lo at 0v state). Again, not sure why.

I checked wiring and reloaded the 30Hz code just to compare and did not have either problem. Net, the LED no longer comes on at differing brightness levels. Strange, can't think of an obvious cause.

So massive progress but no quite there. I'll try a few code preset value adjustments and see what happens.