Arduino Uno LED Strobe changing the duty cycle PWM

Hello, I am using an arduino uno to make a variable blinking speed strobe light. I currently have the code working, but I am looking for some help to change from 50% duty cycle. Below is the code that I have currently.

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

Encoder myEnc(14,15);

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);

unsigned long startMillis; 
unsigned long currentMillis;
float fpm;
const byte ledPin = 13;

void setup()
{
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  startMillis = millis();  //initial start time

  lcd.setBacklightPin(3,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("RPM = ");
  lcd.setCursor(0,1);
  lcd.print("Temp (F) = ");
  lcd.setCursor(20,0);
  lcd.print("Bearing Life = ");
  myEnc.write(5000);
}

long oldPeriod = -999;
long newPeriod = 0;

void loop()
{
  unsigned long period = myEnc.read();
  if (period != oldPeriod){
    oldPeriod = period;
    lcd.setCursor(6,0);
    fpm = 60.0*1000.0/period;
    lcd.print(fpm);
    lcd.print("     ");
  }
  currentMillis = millis();
  if (currentMillis - startMillis >= period)
  {
    digitalWrite(ledPin, !digitalRead(ledPin));
    startMillis = currentMillis;
  }
  Serial.println(fpm);
}

The line below currently has the light on and off for 5 seconds, but essentially what I want is to have it blink quickly on/off every 5 seconds rather than have. I found this link which is what helped me determine what I was looking for, but now I am having trouble implementing it.

myEnc.write(5000);

Any help is appreciated.

IMG_0335 (1).jpg

MODERATOR: Edit image to show it using tags

To change duty cycle use analogWrite(pin, duty_cycle) where duty_cycle is between 0 and 255.

0% is 0
50% is 127
100% is 255

use map(value, fromLow, fromHigh, toLow, toHigh) to convert from your period min/max to 0/255

Looking at the image you posted am I correct to assume that the 'LOW' time is you period and that the 'HIGH' time is a fixed duration.

If that's the all you need to do is modify this part of the code:

  currentMillis = millis();
  if (currentMillis - startMillis >= period)
  {
    digitalWrite(ledPin, !digitalRead(ledPin));
    startMillis = currentMillis;
  }

to this maybe:

  currentMillis = millis(); //no need for this really. can do this directly in the if statement
  if (millis() - startMillis >= period && digitalRead(ledPin)== LOW)
  {
    digitalWrite(ledPin, HIGH); 
  }
  else if(millis() - startMillis >= period+ONTIME) //where ONTIME is your defined ON time
   {
     digitalWrite(ledPin, LOW); 
     startMillis = currentMillis;
   }

How will this "on time" affect the light when I need to reach faster speeds? I'm looking to have it blink up to 83 Hz (5000 flashes per minute) so I don't want to specify an "on time" that isn't possible at higher speeds. Maybe make "on time" = 12 milliseconds because that is the max speed possible?

in my first reply (post #2) I made an assumption which I would appreciate can confirm as correct:

"Looking at the image you posted am I correct to assume that the 'LOW' time is you period and that the 'HIGH' time is a fixed duration?"

if you want to blink at 83Hz and you set ONTIME as 12ms then you actually will be blinking at 83/2 = 41.5Hz (12ms calc period + 12ms ONTIME)

based on you reply, it look like what you want is NOT a fixed ONtime but rather a fixed DUTY CYCLE ie ONTIME = x*period, OFFTIME =(1-x)*period. where 'x' if your duty cycle.

not really complicated to code in OR you could do use AnalogWrite as suggested by surbyte.

Hi,
To increase the strobe "flash rate" you need to increase the FREQUENCY of the output, not the duty cycle.

What is the encoder doing?

Changing the duty cycle will just change the apparent brightness of the strobe.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom.. :slight_smile:

TomGeorge:
Hi,
To increase the strobe "flash rate" you need to increase the FREQUENCY of the output, not the duty cycle.

What is the encoder doing?

Changing the duty cycle will just change the apparent brightness of the strobe.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom.. :slight_smile:

Currently, the encoder is changing how fast the strobe blinks by decreasing "period", however, the way the code is currently written says that for there to be a minute in between flashes, the led must stay on for a minute. Essentially the time is the same value for "on" as well as "off". I need to distinguish a different variable for "on" and "off" I'm thinking in order to fix the "on-time" to the max value I need for 5000 flashes per minute while only adjusting the frequency or time in between flashes.

sherzaad:
in my first reply (post #2) I made an assumption which I would appreciate can confirm as correct:

“Looking at the image you posted am I correct to assume that the ‘LOW’ time is you period and that the ‘HIGH’ time is a fixed duration?”

if you want to blink at 83Hz and you set ONTIME as 12ms then you actually will be blinking at 83/2 = 41.5Hz (12ms calc period + 12ms ONTIME)

based on you reply, it look like what you want is NOT a fixed ONtime but rather a fixed DUTY CYCLE ie ONTIME = x*period, OFFTIME =(1-x)*period. where ‘x’ if your duty cycle.

not really complicated to code in OR you could do use AnalogWrite as suggested by surbyte.

I think you’re helping me and putting me in the right direction here! My own personal opinion is that I should have a fixed time of “on” where turning the encoder changes how frequent it blink, what do you think? I took your opinion and changed the last part of my code, but I’m running into some issues. I get it to blink to whatever speed I want using the “myEnc.write()” command, however the lower flashes I go, the harder it is to increase blinking speed (more turns on the encoder). What I want is to be able to increase by 1 flash per minute with the eventual process of two encoders (fine and coarse adjustment), but I’m struggling with the first encoder.

I had a look at the encoder.h library and I think I understand what you a doing at the moment.

you are using Enc.write to set the encoder position.. ie you are not using a physical encoder yet, is that correct?

Not sure what Enc.read outputs though. If it is position (ie number of steps), then with every Enc.read you need to subtract against the previous value to know how many steps were made ie:

Newposition = Enc.read

new_step_cnt = Newposition - Old position //this is a signed value btw!

Oldposition = Newposition

what I would then suggest is that you set a base period ie 1 pulse per minute = 60000ms //maybe set your LED pulse to 6ms if you want a fixed ONTIME

devisor = old_step_cnt + new_step_cnt

period = baseperiod/devisor //careful about dividing by zero and negative numbers!

old_step_cnt = new_step_cnt

offtime = period - ONTIME

Hope its not too confusing! :slight_smile:

sherzaad:
I had a look at the encoder.h library and I think I understand what you a doing at the moment.

you are using Enc.write to set the encoder position.. ie you are not using a physical encoder yet, is that correct?

Not sure what Enc.read outputs though. If it is position (ie number of steps), then with every Enc.read you need to subtract against the previous value to know how many steps were made ie:

I am actually currently using an encoder :slight_smile: I am using Enc.write to set it to an initial start position (1 flash per minute) with the Enc.read output of time in between flashes, currently, I have 1 minute wait in between flashes. I'm a little confused with the necessity of the number of steps made and what that really does, but I'm open to all suggestions! According to your previous post (#4) I think 6ms seems to be a good amount of "ontime" to reach 83Hz.

Currently, my issue is the increments at which the encoder is turning at. For example, it takes a lot of encoder rotations for the fpm to change to 1.01 and eventually 1.02. I'm not sure how to program the increments to change smoothly in 1 flash per minute increments.

Thanks for your help so far!

predzZzZzZ:
I am actually currently using an encoder :slight_smile: I am using Enc.write to set it to an initial start position (1 flash per minute) with the Enc.read output of time in between flashes, currently, I have 1 minute wait in between flashes. I'm a little confused with the necessity of the number of steps made and what that really does, but I'm open to all suggestions! According to your previous post (#4) I think 6ms seems to be a good amount of "ontime" to reach 83Hz.

Currently, my issue is the increments at which the encoder is turning at. For example, it takes a lot of encoder rotations for the fpm to change to 1.01 and eventually 1.02. I'm not sure how to program the increments to change smoothly in 1 flash per minute increments.

Thanks for your help so far!

knowing how many steps have been done tells your arduino whether to increase (incrementing) or decrease the flash rate. with my code in theory, (as I cannot test it!) you would not need to initialise the positioncounter.
ie:
initially no increment/decrement -> blink off
increment by 1 step -> 1 flash per minute
increment by 2 steps -> 1+2 = 3 flashesper minute
.
.
.
.
total number of steps = 5000 -> 5000 flashes per minute

sherzaad:
Newposition = Enc.read

new_step_cnt = Newposition - Old position //this is a signed value btw!

Oldposition = Newposition

what I would then suggest is that you set a base period ie 1 pulse per minute = 60000ms //maybe set your LED pulse to 6ms if you want a fixed ONTIME

devisor = old_step_cnt + new_step_cnt

period = baseperiod/devisor //careful about dividing by zero and negative numbers!

old_step_cnt = new_step_cnt

offtime = period - ONTIME

I'm not very good at coding, but how does this change the code that I have currently? Do I need to get rid of my variable "period" and put all of this in an "if" statement? I'm a little confused. I also noticed a lot of variables that weren't established, such as Oldposition before subtracting it from Newposition.

Also, I think what you're saying is the increment values are changing each time? I only want it to change by 1 solidified increment each click (i.e 1 step only), just for clarification.

predzZzZzZ:
Also, I think what you’re saying is the increment values are changing each time? I only want it to change by 1 solidified increment each click (i.e 1 step only), just for clarification.

you may need to print the myEnc.read() to serial monitor to confirm this but reading the library I think that myEnc.read() returns a counter that accumulates the number of steps.

That counter increments/decrements depending one how to rotate the encoder i.e incrementing/decrementing depends on the number of clicks you do (if the library is working correctly then 1 click MEANs incrementing/decrementing the counter by 1)

I moded your code somewhat to match what I have in mind. I’ve also swapped millis() for micros() as the former has a resolution of 4ms if I recall well.

Can’t guarantee that may code will work as I cannot test it but your original code is fairly close to what I have in mind and coded.

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

Encoder myEnc(14,15);

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);

const byte ledPin = 13;
const unsigned long base_period_us = 60000000;
const unsigned int OnTime_us = 6000;

unsigned long startMicros=0; 
unsigned long currentMicros=0;
int Oldposition=0;
int OldStepCnt=0;
float fpm;
float offTime_us;

void setup()
{
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  startMicros = micros();  //initial start time

  lcd.setBacklightPin(3,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("RPM = ");
  lcd.setCursor(0,1);
  lcd.print("Temp (F) = ");
  lcd.setCursor(20,0);
  lcd.print("Bearing Life = ");
  //myEnc.write(5000);
}

void loop()
{
  int NewStepCnt = 0;
  int NewPosition = myEnc.read();
  unsigned long t_delta;
  
  if (NewPosition != Oldposition){
    newStepCnt = NewPosition - Oldposition; 
    lcd.setCursor(6,0);
	if(newStepCnt>0) fpm = base_period_us/(OldStepCnt+NewStepCnt);
	else fpm = 0;
	Serial.println(fpm);
    lcd.print(fpm);
    lcd.print("     ");
	OldStepCnt = NewStepCnt;
	offTime_us = fpm - OnTime_us;
  }
  
  currentMicros = micros();
  t_delta = currentMicros - startMicros;
  
  if (t_delta > offTime_us && t_delta<= fpm)
  {
    digitalWrite(ledPin, HIGH);
  }
  else if (digitalRead(ledPin)== HIGH){
	digitalWrite(ledPin, LOW);
    startMicros = currentMicros;
  }
  else{
	digitalWrite(ledPin, LOW);
  }
  
}

Wow! Thank you for taking the time to write all of that code for me! I decided to give it a try, however, the LED doesn't blink and the LCD/serial monitor always prints "0.00" for fpm. I'm assuming it's stuck in the if statement:

if(newStepCnt>0) fpm = base_period_us/(OldStepCnt+NewStepCnt);
	else fpm = 0;

I decided to do some research and I found this link (Arduino Playground - TimingRollover) I think this is similar to what we were trying to do, however, I read somewhere once that micros() is not a good timing variable as opposed to millis() however I could be incorrect. Thank you for the code again, however, I am currently diagnosing the issues. I had to initialize "signed newStepCnt", but that still didn't help. If you have any ideas I'm all ears. Thanks!