Freq Generator Not Reaching Required Freq & Continuing At That Freq

Hi!

I have an Uno board with a motor control shield. I am using the Arduino IDE 1.8.9. Each of the four motor outputs has an LED connected for prototyping reasons. My hardware arrangement is completely deliberate, so nothing much will be changed with that. The LEDs blink in sequence to create an almost circular pattern, and I was able to make the sequence accelerate gradually using delays and pre-set variables. What I would like to do, is have the user input the Hertz that they want to produce using the serial monitor with a float input. The program should divide 1 by that input to create the time delay value for the sequence. For some reason, I can’t figure out why the time delay doesn’t stop at the frequency that the user has requested. Instead, it continues to accelerate until the time delay is 0. At one point, the time delay eventually went past 0, and into negative numbers. What should happen is: it should accelerate until the frequency that the user has requested, and then continue to run at that frequency forever (unless the power is cut of course). The result I am looking for is: the user inputs a frequency for the sequence, the sequence starts at a preset value of maybe 500ms, the speed of the sequence increases until the frequency of the sequence matches what the user requested, the sequence then continues at this frequency forever. I need this code to run as efficiently as possible by the way, and it needs to be able to run upto really high frequencies (and the frequency needs to be to at least 2 decimal places). Sorry about the ameature approach with this code. This is my first time doing anything like this, and I am learning as I go along. I’ve definately thrown myself in the deep end.

This is my first post on a forum, so I am not completely familiar with how to format this topic. For that reason, I’ve just attached the code below as well.

Any help is greatly appreciated.

#include <AFMotor.h>
AF_DCMotor A(1);
AF_DCMotor B(2);
AF_DCMotor C(3);
AF_DCMotor D(4);
float timer;
float userTimer;
float accelleration;
float spd;
float userInput;

//float spd = Serial.read();

void setup()
{
  Serial.begin(9600);
  spd = Serial.println("Please Enter Speed (Hz)");
  spd = Serial.read();
  userTimer = (1 / (spd));
  accelleration = (0.0001);
  timer = (1);
  A.setSpeed(255);
  B.setSpeed(255);
  C.setSpeed(255);
  D.setSpeed(255);
}

void loop()
{
  if (Serial.available())
  {
    if (timer > userTimer)
    {
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        A.run(FORWARD);
          delay(timer);
        A.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        B.run(FORWARD);
          delay(timer);
        B.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        C.run(FORWARD);
         delay(timer);
        C.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        D.run(FORWARD);
          delay(timer);
        D.run(BACKWARD);
    }
    else if (timer <= userTimer)
    {
      Serial.print(userTimer);
      Serial.print('\n');
        A.run(FORWARD);
          delay(userTimer);
        A.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        B.run(FORWARD);
          delay(userTimer);
        B.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        C.run(FORWARD);
          delay(userTimer);
        C.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        D.run(FORWARD);
          delay(userTimer);
        D.run(BACKWARD);
    }
    else
    {
      Serial.print(userTimer);
      Serial.print('\n');
        A.run(FORWARD);
          delay(userTimer);
        A.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        B.run(FORWARD);
          delay(userTimer);
        B.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        C.run(FORWARD);
          delay(userTimer);
        C.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        D.run(FORWARD);
          delay(userTimer);
        D.run(BACKWARD);
    }
  }
}

Code1.ino (2.28 KB)

  spd = Serial.read();

Reads 1 byte from the Serial monitor. Have you tried printing spd to ensure that it is the value that you expect ?

Why does all of the code in loop() depend on serial data being available even though you never read it ?

What have you got the line ending set to in the Serial monitor, bearing in mind that your code only ever reads 1 byte ?

I do apologise, the code isnt working at all suddenly. The error message im recieving is:

Arduino: 1.8.9 (Mac OS X), Board: "Arduino/Genuino Uno"

location: In function 'void setup()': Code1:16:6: error: redefinition of 'void setup()' void setup() ^ location:16:6: note: 'void setup()' previously defined here void setup() ^ location: In function 'void loop()': Code1:39:6: error: redefinition of 'void loop()' void loop() ^ location:30:6: note: 'void loop()' previously defined here void loop() ^ exit status 1 redefinition of 'void setup()'

This report would have more information with "Show verbose output during compilation" option enabled in File -> Preferences.

I can' t check spd until this is now fixed. I expect spd will not give what I expected. I thought that the single character from serial will be one of the problems. The reason I used serial available to start the loop, is to see if that was a problem. I thought maybe it is because I might be sending data before serial is available (this can't do any harm though right?). I haven't set a line ending for the serial. If the serial is such a problem, is there any easier way of inputting a value to the Arduino whilst it is running?

My guess would be that you have code in 2 tabs in the IDE unless you really do have 2 loop() and setup() functions in your sketch

So what would I have in each tab if I was to try that? Also, what would the benefit be? And will they both run at the same time (will that decrease the reliability/ speed of it, or will one run before the other?)

The compiler is telling that you cannot have two setups and two loops, so how they might perform is moot.

Why does it think that I have two setups & two loops? There's only one as far as I can see. Also, is there any simple way of reading a multiple digit float from serial?

The compiler can see two. That's why it is bitching.

I can't see any.

George-Mathieson: Why does it think that I have two setups & two loops? There's only one as far as I can see. Also, is there any simple way of reading a multiple digit float from serial?

How many tabs have you got open in the IDE ?

Search for loop in the IDE and click "Search all Sketch Tabs" in the find dialogue. How many does it find and where are they ?

Right, so I was rather daft. There were indeed two tabs open (both running the exact same code). That error is now gone, but the other problems still occur of course. When I ask it to print spd in serial, it prints the value "-1.0" before I have even inputted a value. I assume it is taking that value from the "P" in "Please Enter Speed (Hertz)". Any ideas?

I should be able to enter the value "2" (this should now be spd) into the serial monitor, and it should divide 1 by 2 to make 0.5 (that should now be the value of userTimer in seconds). To print the value of spd, I added this to line 26 (in the setup void):

Serial.println(spd);

I've just read - on another Arduino topic - that a union structure should be used to read multiple bytes of data from a serial input. It apparently combines the serial data into a single integer (I assume I would be able to turn it into a float as well). What I would really like, is for the user to input a float such as 44.7 (Hz) for the Arduino to convert into the time delay. That should then make the 4 LED sequence run at 44.7Hz. Do you know anything about a union structure because he didn' go into much detail on the one that I have just read. Google doesn't seem to find any relevant and easier to understand results either. I believe that if I sort out the serial input, the spd should correct itself. If possible, I wouldn't want any serial end character (for ease of use). The user should just be able to input a float and hit enter.

You can't use a union to convert ASCII input like "44.7" to the equivalent float.

That approach is a dead-end.

I wouldn't want any serial end character (for ease of use). The user should just be able to input a float and hit enter.

In which case, the "enter" is the "serial end character"

Of course! I'll use the enter key as the serial end. Could you send me some example code for me to implement to mine using this, please?

When I ask it to print spd in serial, it prints the value "-1.0" before I have even inputted a value.

Here is your setup() function

void setup()
{
  Serial.begin(9600);
  spd = Serial.println("Please Enter Speed (Hz)");
  spd = Serial.read();
  userTimer = (1 / (spd));
  accelleration = (0.0001);
  timer = (1);
  A.setSpeed(255);
  B.setSpeed(255);
  C.setSpeed(255);
  D.setSpeed(255);
}

The Serial.read() will occur within microseconds of the program starting and there will, therefore, be no opportunity for the user to enter a value. When there is nothing available to read then Serial.read() returns -1, which is what you are seeing.

One way way to get round this is to do something like this in setup()

while (Serial.available() == 0);  //wait for serial input
spd = Serial.read();

Ah, Perfect! So that would be at the start of my void loop, then when an input is entered, the if loops start?

Robin2 has an excellent serial handling basics thread.

George-Mathieson: Ah, Perfect! So that would be at the start of my void loop, then when an input is entered, the if loops start?

No, as I said, it would go into setup() so that the user is prompted when the program starts. If you want the user to be able to enter a value whilst loop() is running then you must not have a blocking while loop as in my suggested code. Instead you test whether serial data is available and if so you read it and use the value entered.

Please stop referring to "if loops". There is no such thing. The if statement tests whether the condition being tested is true or false which allows you to test for true/false and take action based on it. The if statement does not hang around waiting for something to happen

I completely agree! I've actually been reading Robin2's thread.

Very true, apologies for the technical inaccuracies. So if I put your code in my setup, it will wait until the user has input a value before it moves on to my loop?

So if I put your code in my setup, it will wait until the user has input a value before it moves on to my loop?

Yes, but print the value after receiving it to ensure that it is what you expect. What range of values do you want entered ?

Right, so I’ve noticed a definite improvement: when I input 2 to the serial monitor, spd changes to 50.0 for some reason, then the program works to a point because it accelerates to 0.2 (which is 1/50) and continues at 0.2. I don’t understand why it interprets input 2 as 50.0. I’ll add the current code below:

AF_DCMotor A(1);
AF_DCMotor B(2);
AF_DCMotor C(3);
AF_DCMotor D(4);
float timer;
float userTimer;
float accelleration;
float spd = 0;
float userInput;

//float spd = Serial.read();

void setup()
{
  Serial.begin(9600);
  Serial.println("Please Enter Speed (Hz)");
  while (Serial.available() == 0);  //wait for serial input
  spd = Serial.read();
  userTimer = (1 / (spd));
  accelleration = (0.0001);
  timer = (1);
  A.setSpeed(255);
  B.setSpeed(255);
  C.setSpeed(255);
  D.setSpeed(255);
  Serial.println(spd);
}

void loop()
{
  if (Serial.available())
  {
    if (timer > userTimer)
    {
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        A.run(FORWARD);
          delay(timer);
        A.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        B.run(FORWARD);
          delay(timer);
        B.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        C.run(FORWARD);
         delay(timer);
        C.run(BACKWARD);
      Serial.print(timer);
      Serial.print('\n');
      timer = timer - accelleration;
        D.run(FORWARD);
          delay(timer);
        D.run(BACKWARD);
    }
    else if (timer <= userTimer)
    {
      Serial.print(userTimer);
      Serial.print('\n');
        A.run(FORWARD);
          delay(userTimer);
        A.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        B.run(FORWARD);
          delay(userTimer);
        B.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        C.run(FORWARD);
          delay(userTimer);
        C.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        D.run(FORWARD);
          delay(userTimer);
        D.run(BACKWARD);
    }
    else
    {
      Serial.print(userTimer);
      Serial.print('\n');
        A.run(FORWARD);
          delay(userTimer);
        A.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        B.run(FORWARD);
          delay(userTimer);
        B.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        C.run(FORWARD);
          delay(userTimer);
        C.run(BACKWARD);
      Serial.print(userTimer);
      Serial.print('\n');
        D.run(FORWARD);
          delay(userTimer);
        D.run(BACKWARD);
    }
  }
}

(The range of values to be entered is probably 0.001ms to 1million ms.)