Help with millis(), and writing voltage through MOSFET.

Hello, I have a bit of code that works with the current circuit I have, but I'd like some assistance altering the code/ circuit.

This is my circuit:

Here is my code:

int lightSensorPin = A0;
int motorPin = 2;
//int dayValue = 0;
int nightValue = 10;

void setup() {
  Serial.begin(9600);
  pinMode(lightSensorPin, INPUT);
  pinMode(motorPin, OUTPUT);

}

void loop() {
  int lightSensorValue = analogRead(lightSensorPin);
  Serial.println(lightSensorValue);

  if (lightSensorValue < nightValue) {
    digitalWrite(motorPin, 50);
  }
  else {
    digitalWrite(motorPin, LOW);
  }

}
  1. I was told that this MOSFET was not particularly good for this design, but I use it anyway since it's the only one I currently have. Is there any way that I am able to, by changing the circuit or altering code, send a lower voltage through the (digital 2) pin, and slow down the motor besides having it spin full blast? I tried changing digitalWrite(motorPin, 50) to a lower value other than "HIGH", but that didn't seem to do anything regarding slowing the motor down.

  2. Also, is there any way to reverse the motor spin direction, again, by either changing the circuit or altering the code? I'd like the motor to spin one way when the variable "nightValue" is reached, and another way when the variable "dayValue" is reached.

  3. And lastly, how can I use millis() to make sure that the motor spins for only x amount of time after reaching one of the variables mentioned above? The only time it should spin again afterward, hopefully in the opposite direction, is when the other variable is read?

Example: Motor spins left for 5 seconds at dusk when (nightValue) is reached. Afterward, in about 8 hours, the motor spins right for 5 seconds in the morning when (dayValue) is reached.

I know the 3rd option is possible, not so much about the first two.

how can I use millis() to make sure that the motor spins for only x amount of time after reaching one of the variables mentioned above?

Start by reading Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

In essence, save the millis() value at the time that the start action happens. Then, each time through loop(), check whether the required wait period has elapsed by subtracting the start time from the millis() value now. If the period has elapsed then act accordingly and maybe save the start time for the next activity. If not, then go round loop() again, perhaps taking other actions and/or reading inputs, but don’t block the free running of loop().

Hi

toxicxarrow:

  1. I was told that this MOSFET was not particularly good for this design, but I use it anyway since it's the only one I currently have. Is there any way that I am able to, by changing the circuit or altering code, send a lower voltage through the (digital 2) pin, and slow down the motor besides having it spin full blast? I tried changing digitalWrite(motorPin, 50) to a lower value other than "HIGH", but that didn't seem to do anything regarding slowing the motor down.

You're almost there
Use a PWM output (for example pin 9). Do a google search for "using pwm to control dc motor speed arduino" there are a couple of good explanations. Doing some research on PWM output on the Arduino will help as well.
There may need to be a couple of small adjustments to your circuit to implement this, but nothing more than adding a resistor, a capacitor and possibly a diode. These adjustments will be defined by whichever MOSFET and motor you are using.

toxicxarrow:
2) Also, is there any way to reverse the motor spin direction, again, by either changing the circuit or altering the code? I'd like the motor to spin one way when the variable "nightValue" is reached, and another way when the variable "dayValue" is reached.

The easiest method would use a driver chip (eg L293D). The would actually remove the need for the MOSFET.
Again, do a search for "arduino reverse dc motor". You will find a couple of tutorials that utilise pwm control, L293D to control motor speed and reversal.
Other methods (ie incorporating relays) are options, but I'm not sure how they would work with the requirement for variable speed.

@UKHeliBob Thanks for the suggestion. using millis() seemed more difficult at first; but it looks pretty straightforward.

@darrob Thank you for the tips. I completely forgot that PWM is only available on certain pins. I'll look into the driver chip.

Cheers, boys.

using millis() seemed more difficult at first; but it looks pretty straightforward.

As you have discovered, using millis() for timing is not difficult but it can seem quite convoluted at first. The worst problem is when someone has written a program littered with delay()s and asks how to replace them with millis() timing.

@UKHeliBob So, this seems right to me, but the motor never stops spinning once it starts. What am I doing wrong? Also, the Serial Monitor gets stuck on the last value it was outputting after being triggered. Does that have something to do with it?

int lightSensorPin = A0;
int motorPin = 3; //~PWM
const unsigned long waitTime = 2000;
unsigned long currentTime;
unsigned long startTime;

//int dayValue = 0;
int nightValue = 1;

void setup() {
  Serial.begin(9600);
  pinMode(lightSensorPin, INPUT);
  pinMode(motorPin, OUTPUT);
  startTime = millis();

}

void loop() {

  int lightSensorValue = analogRead(lightSensorPin);
  Serial.println(lightSensorValue);
  
  if (lightSensorValue < nightValue) {
    currentTime = millis();
    while(waitTime <= startTime - currentTime) {
      analogWrite(motorPin, 150);
    }
  }else {
      digitalWrite(motorPin, LOW);
  }
  startTime = currentTime;
}
    while (waitTime <= startTime - currentTime)
    {
      analogWrite(motorPin, 150);
    }

How will the program ever exit this while loop ? None of the variables in the condition change in the while loop. Do not use a while loop, let the loop() function do the work

Save the millis() value at the time that the start action happens. Then, each time through loop(), check whether the required wait period has elapsed by subtracting the start time from the millis() value now. If the period has elapsed then act accordingly and maybe save the start time for the next activity. If not, then go round loop() again, perhaps taking other actions and/or reading inputs, but don’t block the free running of loop().

UKHeliBob:
How will the program ever exit this while loop ?

I'd like it to exit the loop after 5 seconds have been elapsed from the starting point. So why in my while loop, does the motor keep spinning after the 5 seconds have passed?

My idea is to tell the Arduino:

If the sensor goes off, start spinning the motor. While the motor is spinning, count five seconds then stop the motor and start checking for other instructions.

While is literally in the instructions I want to give, but I shouldn't use the while loop?

Here is some pseudo code that uses loop() to do the repetition in a non blocking way

set a boolean variable to false

start of loop()
  get current time from millis()
  if the start action occurs and the boolean is false
    start something happening
    save the start time from millis()
    set the boolean variable to true
  end if

  if the boolean is true
    if the current time minus the start time >= wait period or the stop action occurs
      stop the something happening
      set the boolean to false
    end if
end of loop()

Give the variables sensible names to make the code readable and use unsigned long for the timing variables

Thank you, @UKHeliBob for helping me out with this. I ended up doing this, and somehow, magically, it worked!

const unsigned long waitTime = 5000;
int motorPower = 255; //from 0 - 255

int lightSensorPin = A0;
int motorPin = 3; //~PWM
unsigned long currentTime;
unsigned long startTime;
int cycleBreak = 0;

//int dayValue = 0;
int nightValue = 2;

void setup() {
  Serial.begin(9600);
  pinMode(lightSensorPin, INPUT);
  pinMode(motorPin, OUTPUT);
  startTime = millis();
  

}

void loop() {
  int lightSensorValue = analogRead(lightSensorPin);
  Serial.println(lightSensorValue);
  
  if (lightSensorValue < nightValue && cycleBreak == 0) {
    currentTime = millis();
    while (millis() - currentTime < waitTime) {
      analogWrite(motorPin, motorPower);
      cycleBreak = 1;
      }
    }
   else {
     digitalWrite(motorPin, LOW);
  }
  
  if (lightSensorValue > 5) {
    cycleBreak = 0;
  }
}

It seems to accomplish what I want it to do. Do you see anything wrong with it, short or long term? For example, after 50 days, millis() isn’t going to roll over and the Arduino won’t explode or anything, right?

   while (millis() - currentTime < waitTime)
    {
      analogWrite(motorPin, motorPower);
      cycleBreak = 1;
    }

Congratulations. You have just invented your own version of the delay() function. Your while loop will block operation of the code until the period ends. If you don’t mind that then you might just as well have used delay() in the first place.

Look at the pseudo code in my previous reply. Not a while or any other blocking code anywhere.

millis() rolls over in 49 and a bit days but that will not cause any problems if you use unsigned long variables and subtraction in the comparison.

So I filled in my variables into your pseudo-code, and got this:

const unsigned long waitTime = 5000;
int motorPower = 255; //from 0 - 255

int lightSensorPin = A0;
int motorPin = 3; //~PWM
unsigned long currentTime;
unsigned long startTime;
int cycleBreak = 0;

//int dayValue = 0;
int nightValue = 5;

void setup() {
  Serial.begin(9600);
  pinMode(lightSensorPin, INPUT);
  pinMode(motorPin, OUTPUT);
  

}

void loop() {
  int lightSensorValue = analogRead(lightSensorPin);
  Serial.println(lightSensorValue);
  
  unsigned long currentTime = millis();
  if (lightSensorValue < nightValue && cycleBreak == 0) {
      analogWrite(motorPin, motorPower);
      startTime = millis();
      cycleBreak = 1;
      }

   if (cycleBreak = 1) {
    if(currentTime - startTime >= waitTime) {
      digitalWrite(motorPin, LOW);
      cycleBreak = 0;
    }
   }
   
  
}

The motor doesn’t start counting the time until the sensor gets un-activated again.

What exactly is my “stop action occurs”? Isn’t my stop action just waiting out x amount of time?

Also, just to clarify, any time in the history of Arduino code, if a while loop is used, it acts as a delay?

  if (cycleBreak = 1)

BONG !

if a while loop is used, it acts as a delay?

Yes. There will be a delay until the exit condition(s) of the while loop are satisfied

What exactly is my "stop action occurs"? Isn't my stop action just waiting out x amount of time?

You don't have an alternative stop action so ignore that part. It covers the case where you might have say a button input to end the wait period early.

UKHeliBob:

  if (cycleBreak = 1)

BONG !

You got me there.

However, the motor still spins non-stop until the light value becomes high again. How would I go about making it stop spinning after x seconds, and only when the light value goes high and then low again, to spin the motor?

if (cycleBreak == 1)

== for comparison
= for assignment of value

However, the motor still spins non-stop until the light value becomes high again

You are testing whether the light value is lower than the threshold, which it will be all night so the timing cycle repeats. What you need to do is to test whether the light value has become lower than the threshold. Look at the StateChangeDetection example in the IDE

UKHeliBob:
You are testing whether the light value is lower than the threshold, which it will be all night so the timing cycle repeats. What you need to do is to test whether the light value has become lower than the threshold. Look at the StateChangeDetection example in the IDE

I got it working a couple days ago. Your pseudo-code helped a lot, practically spot-on. I definitely got a better idea of how to structure the code, even if I may still need a bit of practice. I just wanted to say thanks for helping guide me through the project, and not just giving me the answer.

Unrelated, quick question:
After getting the code to work and soldering the wires together, the circuit works well with Arduino Uno, but fries ever single Arduino Pro Mini it touches. (I've gone through 5 Pro Mini's, and two iterations of the circuit just to make sure I didn't screw the soldering job on the first one.) Any idea why that might be?

Please post a schematic of the circuit used with the Pro Mini

UKHeliBob:
Please post a schematic of the circuit used with the Pro Mini

I made another topic about it on the forum; nobody can really determine the cause. This page should have both a fritz and circuit picture Link. Page one has a fritz on how how it's connected to the Uno.