Code explanation: modified blink without delay (PWM output, sens. input)

Hello everyone,

i'm a beginner in arduino and coding, so i apologize in advance for any silly questions.

I just recently learned that if you want to simulate multitasking you need to get rid of the delay() functionality and start using timers instead. I learned the blink led without delay example and started playing around with multiple outputs "simoultaneously". I have two leds one dc motor, one servo and a temperature sensor.

The main issue is how to change the time on, time off cycle of the dc motor. Basically changing the duty cycle from now 50% to another value. I found this code online, but i don't fully understand it:

#define LED_PIN 13
#define LED_ON 800       //milliseconds
#define LED_OFF 200

unsigned long ms;        //time from millis()
unsigned long msLast;    //last time the LED changed state
boolean ledState;        //current LED state

void setup(void)
{
    pinMode(LED_PIN, OUTPUT);
}

void loop(void)
{
    ms = millis();
    blinkLED();
}

void blinkLED(void)
{
    if (ms - msLast > (ledState ? LED_ON : LED_OFF)) {
        digitalWrite(LED_PIN, ledState = !ledState);
        msLast = ms;
    }
}

This part confuses me

(ledState ? LED_ON : LED_OFF)

(LED_PIN, ledState = !ledState);

can anyone explain what is happening here? I haven't tried the code out yet, because i'd like to first understand it.

Since we're talking about timers, i'd like to ask a few more things. This is a code i wrote for running multiple things at once.

int op1 = 6;
int op2 = 9;
int temp = 0;
int aread;
int rfun;


long intOp1 = 5000;
long intOp2 = 1000;


int op1State = LOW;
int op2State = LOW;


long prevOp1 = 0;
long prevOp2 = 0;

int readTemp()
{
if (millis() - prevOp2 > intOp2) {
    prevOp2 = millis();

    
    aread = analogRead(temp);
    return aread;
    
  }
}


void setup()
{
  Serial.begin(9600);
  pinMode(op1, OUTPUT);
  pinMode(op2, OUTPUT);
}

void loop()
{  
   
   rfun = readTemp();
   Serial.println(rfun);
   if (rfun > 0)
   {
     if (millis() - prevOp1 > intOp1) {
    prevOp1 = millis();
    
    if (op1State == LOW)
      op1State = HIGH;
    else
      op1State = LOW;

    
    digitalWrite(op1, op1State);
   }
   
   
  }
  
  }

the readTemp function runs an analogRead, writes it in the serial monitor and triggers a led blink. If you look at the serial monitor while it's running you get this kind of readout:

0 0 ... 0 0 100 // after one second 0 0 ... 0 0 167 // after another second 0 0 ... 0 0 234 // after another second ...

as i understand it the main loop() basically cycles very fast and the analogRead() takes readings only every second, because the interval for the function is set to 1000 milliseconds. Thing is that the led acts accordingly...meaning it lights up only when the read out is at a certain value (in this case i set it to "above o"). If i set the read interval to let's say 1 millisecond then it acts seemingly constant. Does this mean that the blinking timing is dependent on the sensor read interval and the led interval? If we change the led with a motor, would this mean that the motor get's "double PWM" instructions? 1. run motor when the readout > 0 2. run the motor at desired PWM frequency and duty cycle

With using delay functionality it's more linear and understandable: 1. read sensor 2. if sensor > value 3.run motor at desired PWM

What i'm probably trying to ask...is this the right way to control outputs based on an input signal while multitasking? I'm probably just over-thinking this "problem" or failing to see the big picture.

I greatly appreciate any help or comments. Thanks.

if (ms - msLast > (ledState ? LED_ON : LED_OFF)) {

The "? :" is a bit like an "if else" If "ledState" is true, then the value of the expression on the right is "LED_ON", if "ledState" is false, then the value of the expression is "LED_OFF"

digitalWrite(LED_PIN, ledState = !ledState);

"!" is logical NOT. So, invert the value of "ledState", assign the new value back to ledState, and write that state to the pin.

Equivalent to

ledState = !ledState;
digitalWrite(LED_PIN, ledState);

I think you have asked too many questions at once which makes your post hard to follow. But please don't start another Thread for the same set of questions. Just ask a few at a time.

I don't see the use of analogWrite(xxx) anywhere - that is the command that creates a PWM signal. You can change xxx any time you like. The Arduino will continue to output the last value you have set.

The extended demo of the BWoD technique in several things at a time may provide some ideas.

...R

I was afraid that i might be asking too much at one, yes, but i didn't want to start another thread. The OP is basicaly two part. The first is about the explanation of the code, which i thank you AWOL for the reply.

The second is mostly me thinking out loud rather than a real question, but thank you, i'll check the link provided.

My project basically requires multitasking several things at one. Two independent leds, a servo motor, a DC motor that must be PWM-ed and a temperature sensor that basically dictates everything. Any additional instructions/links are very much appreciated. Thank you.

Hi again,

i've been doing some coding and i managed to get going the multitasking stuff. But i've ran into another problem, that i know how to solve, but don't know how to write the code :blush:

Thing is like this. The temperature sensor controls the peripherals on the outputs.

The first sequence is

if (temperature > 100) { startMotor(); }

which is simple, the annoying part is that when at the treshold 100 degrees +/-5, the motor jitters as in turning on an off because the temperature fluctuates. For example. When at 97 degrees it's of, but at 102 is on. It keeps doing this until a solid above 100 is reached.

The solution would be to make a "delay" as to wait a few moments before triggering the startMotor(); for the temperature to stabilize. But since i'm using timers a classic delay() is a no go. I need to code in the timer loop to not trigger or to delay the temperature readout.

Basically if 100 degrees is reached let's say at 10.000 millis(), the startMotor sequence should wait until the 20.000 millis() mark has been reached (10 seconds after the 100C has been read).

Even better would be if i could delay the temperature read signal rather than each sub action, because the temperature dictates everything.

The code i'm using is simmilar to the OP, or any other basic timer code.

Any pointers how could i code in some delay time? Thanks

you could try something like this (pseudo):

  temperature = //read temp here
  if (temperature <= 100) timerToggle = false;
  if (temperature > 100 && lastTemp <= 100)
  {
    startTime = millis(); 
    timerToggle = true; 
  }
  lastTemp = temperature;
  if (startTime - millis() >= 10000UL && timerToggle = true)// 10 seconds delay
  {
    // start motor??
  }

You need to set up a dead band. Basically your ON temperature and your OFF temperature will be different.

For example : Set turn ON temp to 102 and your OFF temp to, say 98. This would be 4 degree dead band.

temp = readTemp();
If (temp>onTemp) turnCoolingOn();
if (temp<offTemp) turnCoolingOff();

would this work?

-jim lee

@Bulldog

I haven't yet tried it out, but from what i can tell this just might be what i need. I just need to figure out how to implement it into the code. Thanks a lot.

@JimLee

Hmm. Sound simple and effective, but it keep me thinking. Wouldn't then the motor still start behaving jittery, just this time at 102 degrees instead of 100?


Oh and i also like to comment on another thing i was talking in the OP.

When using a timer to take a readout (using simple code) to trigger a PWMed motor you kinda get a "double PWM" effect. At least from what i understand.

Here's what is going on.

The temperature readout interval is set to 1 second. When you run this in a loop and print out to the serial port you get this pattern.

program starts 0 0 ... 0 0 101 //first temperature readout 0 0 ... 0 0 101 // second temperature readout, happening after 1 second due to chosen interval 0 0 ... 0 0 and so forth

If you try to trigger an action that is dictated by this readout...like i did to start a motor after a certain temperature was reached, the motor get's in trouble.

The motor is instructed to start only after reaching 100C, which happens to be only every other second. The rest is just 0 and the motor basically stops in the time between readouts. Hence....you get an unintentional and unwanted PWM effect, besides the intentional one that already operates the speed. I solved this by just increasing the cycle rate of the temperature readouts....from 1 second to 1 millisecond. Thus decreasing the timeout of the motor. Technically it works and you don't even notice it, but the zeroes are still there, just less of them. To completely solve this you have to make the interval so small that it matches the acquisition rate of the arduino inputs...which is what...in the MHz range?.

I gather this is nothing new for the experts, but for the beginners out there like me, i wanted to share this information.

P.S. I was stumbling around the section and found this thread

http://forum.arduino.cc/index.php?topic=257454.0

It's somewhat similar to what bulldog suggested and something that i think i also need.

far_1: @JimLee

Hmm. Sound simple and effective, but it keep me thinking. Wouldn't then the motor still start behaving jittery, just this time at 102 degrees instead of 100?

That depends on how jittery your temperature measurement is. Note that in Jim's example, the motor turns on at 102 degrees, but won't turn off again until 98 degrees, a four degree drop. If your temperature reading just jitters by a degree or two, the motor will operate smoothly. If it jitters more than that, you might need a wider dead band.

Also note that Jim's "dead band" is also known as "hysteresis". You might find more success searching under that term.

Regarding the other stuff you posted about...

int readTemp()
{
if (millis() - prevOp2 > intOp2) {
    prevOp2 = millis();

    
    aread = analogRead(temp);
    return aread;
    
  }
}

This is a big, BIG no-no. You are allowing the program execution to read the end of a non-void function without a return value. If you only want to update the temperature value once every second, move the timing stuff out of this function and into loop. That way, you won't return 0 like it's doing now.

@Jiggy

Regarding hysteresis. Aha i see now, yes.

For the other part. Could you explain a bit more? I'm afraid i don't follow you.

If i move the timing part of the function [i.e. if (millis() - ...] out of it and into the main loop and then calling just the return value...wait. Aha, I think i understand now.

Basically the function has the task of reading and returning a numerical value (hence the int return type) and shouldn't be doing any iterations or any updating data or any other continuous operation. Am i understanding it correctly?

P.S. I've tried it. Works exactly as you said. Thanks, you saved me a lot of trouble. Actually....it's even better than that. With now being able to set the interval without worrying about the zeroes, i can actually increase the readout interval to let's say 10 seconds, which also automatically solves the motor issue when reaching the threshold. With a fast cycle rate the motor would jiter because the temperature readout had a huge resolution, thus catching every value around the 100 degree mark. Now, by the time the next readout comes, the value is safely above the 100 mark. Hehe, two birds with one stone.

Basically the function has the task of reading and returning a numerical value (hence the int return type) and shouldn't be doing any iterations or any updating data or any other continuous operation. Am i understanding it correctly?

The problem is in how you imploemented the function. If you wanted to do the timing in the function (which is perfectly fine), you shoudl have had it updating a global temperature variable instead of returning a value.

As it was, follow the execution of the program line-by-line. When the interval has passed, it returns an analogRead(), no problems. But what happens when the interval hasn't passed? The return is skipped, and the program continues after the if statement. However, there's nothing after the if statement. especially no return value. So the function defaults to returning 0. That's why you got all those zeros in between legit readings.

With proper hysteresis you shouldn't need to make the interval that long to avoid the motor stuttering.

Ah yes, i see it now. Never crossed my mind really, but it’s perfectly logical.

I’m going to implement the hysteresis and also the added interval time was something i needed for another part of the program.
I thank you again. As far as i can tell, all the main issues for my current project are solved now.