Go Down

Topic: Issue with millis and serial printing (Read 113 times) previous topic - next topic

lossev

Hi there!
Thank you for taking the time to look at my problem.

I am trying to make a "dog feeding program" for a school project.

What am I trying to achieve?
I want my program to save an input from serial monitor to a variable (succeeded as far as I see).
This input value represents seconds, as in "in how many seconds do you want to feed your dog?"
I want to use this value, converted to milliseconds, to turn on a small engine for a short time.

Right now I'm using a Serial.println to represent the "engine is turned on"- part of the program.

Now my problem is that the program just spams this Serial.println, instead of waiting for an input. I tried to make the program not spam by adding
Code: [Select]
&& x != 0, but to no avail.

Hope this is enough info to go on, thanks for your help!

Code: [Select]

unsigned long startMillis=0;
unsigned long currentMillis;
char x;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available()>0)
  {
  x = Serial.read();
  Serial.print("The dog will be fed in ");
  Serial.print(x);
  Serial.println(" seconds");
  }
 
  currentMillis = millis();
  if (currentMillis - startMillis >= ((x *1000)&& x != 0))//*1000 to convert seconds to milliseconds
  {
    Serial.println("engine is turned on");
    startMillis = currentMillis;
  }
}

Delta_G

#1
Oct 21, 2019, 07:13 pm Last Edit: Oct 21, 2019, 07:15 pm by Delta_G
Code: [Select]
if (currentMillis - startMillis >= ((x *1000)&& x != 0))

First obvious thing is that you've got the parenthesis messed up here. You've grouped the x != 0 part with (x * 1000).  You should probably have the whole time comparison in one group and the x != 0 separated completely:

Code: [Select]
if ((currentMillis - startMillis >= (x *1000))&& (x != 0))


Code: [Select]
{
  x = Serial.read();
  Serial.print("The dog will be fed in ");
  Serial.print(x);
  Serial.println(" seconds");
  }


If this will be when the timing should start, then startMillis should probably be set in there. 



I'd also have a boolean called needToFeedDog and set it to true when I want the dog fed and back to false once he is fed.  Then the timing code goes in an if statement so it only runs if the dog needs to be fed (ie needToFeedDog is true)

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

lossev

#2
Oct 21, 2019, 07:46 pm Last Edit: Oct 21, 2019, 07:49 pm by lossev Reason: added the full code at the end and fixed a small thing
First obvious thing is that you've got the parenthesis messed up here. You've grouped the x != 0 part with (x * 1000).  You should probably have the whole time comparison in one group and the x != 0 separated completely:

Code: [Select]
if ((currentMillis - startMillis >= (x *1000))&& (x != 0))

Thanks, it has been fixed.


Code: [Select]
{
  x = Serial.read();
  Serial.print("The dog will be fed in ");
  Serial.print(x);
  Serial.println(" seconds");
  }


If this will be when the timing should start, then startMillis should probably be set in there.  

Well, the thought is that startMillis is just set to 0 at first as stated at the top of the code. Then the if statement runs. It should update the startMillis value to the currentMillis when it's through the if statement and then feed the dog again and again after another x seconds

Code: [Select]

  if(needToFeedDog = true){
    if ((currentMillis - startMillis >= (x *1000))&& (x != 0))//*1000 to convert seconds to milliseconds
    {
    Serial.println("engine is turned on for y seconds"); //representing feeding
    startMillis = currentMillis;
    }
  needToFeedDog = false;
  }


Also, I added the bool, but I'm not sure what to do with it? I'm planning to have the dog fed at a set time. Let's say I put in 5 seconds, then I want it looped, over and over again. Every five seconds the dog should be fed (should actually be hours, not seconds, but that's a lot of waiting to test the code)
If you can help me code this in a better way I would be very grateful
:)

I'd also have a boolean called needToFeedDog and set it to true when I want the dog fed and back to false once he is fed.  Then the timing code goes in an if statement so it only runs if the dog needs to be fed (ie needToFeedDog is true)


So the full code now is the following:
Code: [Select]

unsigned long startMillis=0;
unsigned long currentMillis;
char x;
bool needToFeedDog = true;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available()>0)
  {
  x = Serial.read();
  Serial.print("The dog will be fed in ");
  Serial.print(x);
  Serial.println(" seconds");
  }
  
  currentMillis = millis();
  if(needToFeedDog = true){
    if ((currentMillis - startMillis >= (x *1000))&& (x != 0))//*1000 to convert seconds to milliseconds
    {
    Serial.println("engine is turned on for y seconds"); //representing feeding
    startMillis = currentMillis;
    }
  needToFeedDog = false;
  }
}

Delta_G

Quote
Well, the thought is that startMillis is just set to 0 at first as stated at the top of the code. Then the if statement runs. It should update the startMillis value to the currentMillis when it's through the if statement and then feed the dog again and again after another x seconds
So you want the feeding to start immediately once the user enters a number?  That's fine it's just not what the display says. 

Code: [Select]
if(needToFeedDog = true){

= vs ==.   


|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

sterretje

I post this as addition to Delta_G's first reply to indicate some possible issues that you might encounter.

You have your (s and )s wrong in the if statement (probably after adding the x!=0)

You can try this
Code: [Select]
if (x != 0 && (currentMillis - startMillis >= x * 1000))


Next problem is that the compiler by default does calculations with 16 bit; you need to force it to use unsigned longs.

Change the first part of loop() to make the problem visible
Code: [Select]
if (Serial.available() > 0)
  {
    x = Serial.read();
    Serial.print("The dog will be fed in ");
    Serial.print(x * 1000);
    Serial.println(" milliseconds");
  }

The result will surprise you ;) Next change it to force calculations with unsigned longs.
Code: [Select]
if (Serial.available() > 0)
  {
    x = Serial.read();
    Serial.print("The dog will be fed in ");
    Serial.print(x * 1000UL);
    Serial.println(" milliseconds");
  }

The result will still surprise you because  receive a char; so if you send '1', the Arduino receives 0x31 (49 decimal) so the time will be 49 seconds. You can subtract '0' before multiplying
Code: [Select]

  if (x !=0 && (currentMillis - startMillis >= (x - '0') * 1000)) //*1000 to convert seconds to milliseconds


I'll leave the polishing to you ;)
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

lossev

#5
Oct 21, 2019, 08:16 pm Last Edit: Oct 21, 2019, 08:17 pm by lossev
So you want the feeding to start immediately once the user enters a number?  That's fine it's just not what the display says. 

Well maybe it's my poor understanding of code, but I want the following to happen:

1) user puts in a number, x, in the serial monitor (between 0-60).
2) This number is being printed with the text "The dog will be fed in x seconds".
3) after x seconds has passed, an engine will turn on, signifying that food is being poured.



Code: [Select]
if(needToFeedDog = true){

= vs ==.   

fixed this


lossev

I post this as addition to Delta_G's first reply to indicate some possible issues that you might encounter.

You have your (s and )s wrong in the if statement (probably after adding the x!=0)

You can try this
Code: [Select]
if (x != 0 && (currentMillis - startMillis >= x * 1000))


Thanks, I changed my code to your version :)

Next problem is that the compiler by default does calculations with 16 bit; you need to force it to use unsigned longs.

Change the first part of loop() to make the problem visible
Code: [Select]
if (Serial.available() > 0)
  {
    x = Serial.read();
    Serial.print("The dog will be fed in ");
    Serial.print(x * 1000);
    Serial.println(" milliseconds");
  }

The result will surprise you ;) Next change it to force calculations with unsigned longs.
Hm, interesting, I got a negative value :O

Code: [Select]
if (Serial.available() > 0)
  {
    x = Serial.read();
    Serial.print("The dog will be fed in ");
    Serial.print(x * 1000UL);
    Serial.println(" milliseconds");
  }

The result will still surprise you because  receive a char; so if you send '1', the Arduino receives 0x31 (49 decimal) so the time will be 49 seconds. You can subtract '0' before multiplying
Code: [Select]

  if (x !=0 && (currentMillis - startMillis >= (x - '0') * 1000)) //*1000 to convert seconds to milliseconds


I tried changing it to UL, but times 1 instead of 1000 because I want to maintain "seconds", but the program does not print my "engine is turned on for y seconds"- text. And the (x - '0') doesn't seem to do anything for me.

I'll leave the polishing to you ;)
Oh boy... :P
My full code thus far:

Code: [Select]

unsigned long startMillis;
unsigned long currentMillis;
char x;
bool needToFeedDog = true;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available()>0)
  {
  x = Serial.read();
  Serial.print("The dog will be fed in ");
  Serial.print(x * 1UL);
  Serial.println(" seconds");
  }
 
  currentMillis = millis();
  if(needToFeedDog == true)
  {
    if (x != 0 && (currentMillis - startMillis >= (x - '0') * 1000))//*1000 to convert seconds to milliseconds
    {
    Serial.println("engine is turned on for y seconds"); //representing feeding
    startMillis = currentMillis;
    }
   
  needToFeedDog = false;
  }
}

TheMemberFormerlyKnownAsAWOL

How long is 'a' seconds?
Or even '?' seconds?
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

lossev

How long is 'a' seconds?
Or even '?' seconds?
Are you referring to the fact that both "a" and "?" can be sent in the serial monitor?

I'm hoping that our teacher will be nice and lets us put only numbers in :)

TheMemberFormerlyKnownAsAWOL

What about pauses between feeding longer than 9 seconds?

Quote
I'm hoping that our teacher will be nice and lets us put only numbers in
Mine never were.
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

lossev

What about pauses between feeding longer than 9 seconds?
I found a tutorial here on the forum by Robin2 called "Serial Input Basics - updated", and he has an example there. "Example 2 - Receiving several characters from the Serial Monitor", I'll try to incorporate that when I can get this basic code to work. So far, no luck on getting the program to print "the engine is turned on".


Mine never were.
Ouf, tough. My course is a pretty small one, like scratching the surface (unless you aim for high grades, which I've come to realize I will not be).

Go Up