How to use serial monitor to input initial set of constants?

Hello and thanks for reading. I've been working on this for a few hours now and haven't really come close to what I want to do. So basically I have 3 motors in a shield, and I want to use the serial monitor to manually set constants, which is the runtime for each motor. Ideally I would like to functionality like in python, raw_input() where it prompts for an input, and after you press enter, your value is saved and used for the rest of the program. I assumed it would be this easy but I guess it's not.
I tried to modify an example sketch to try to get 2 constants and use them to blink leds at different rates. It's just a proof of concept so it doesn't really matter to me if it works or not but the problems I'm experiencing are a). When I type in values, its very inconsistent on whether blinkrate 1 or 2 is being changed, it just seems random even though to me, the void loop() is sequential. Secondly, the integers aren't converting right while it was working fine with just 1 blink function and number input, basically the default sketch. I know the sketch is sloppy but I want to get the concept down. If anyone can shed some light or even just point me to some code where someone used the monitor to set constants that would help tremendously. Thanks for your time.

/*
 * SerialReceive sketch
 * Blink the LED at a rate proportional to the received digit value
*/
const int ledPin = 13; // pin the LED is connected to
const int ledPin2=11;
int   blinkRate=0;     // blink rate stored in this variable
int   blinkRate2=0;
int value;
int value2;
////////////////////////////////////////////
void setup()
{
  Serial.begin(9600); // Initialize serial port to send and receive at 9600 baud
  pinMode(ledPin, OUTPUT); // set this pin as output
}

////////////////////////////////////////////
void loop()
{
  getNum();
  getNum2();
  blink();
  blink2();
}

// blink the LED with the on and off times determined by blinkRate
void blink(){

  for (int i=0;i<100;i++){
    digitalWrite(ledPin,HIGH);
    delay(blinkRate); // delay depends on blinkrate value
    digitalWrite(ledPin,LOW);
    delay(blinkRate);
    break;
}
}
void blink2(){
  for(int i=0;i<100;i++){
    digitalWrite(ledPin2,HIGH);
    delay(blinkRate2);
    digitalWrite(ledPin2,LOW);
    delay(blinkRate2);
    break;
}
}
/////////////////////////////////////////////////////////////////
void getNum() {
   if( Serial.available())
  {
    char ch = Serial.read();
    if( isDigit(ch) )// is this an ascii digit between 0 and 9?
    {
       value = (value * 10) + (ch - '0'); // yes, accumulate the value
    }
    else if (ch == 10)  // is the character the newline character?
    {
       blinkRate = value;  // set blinkrate to the accumulated value
       Serial.print("here is your blinkrate 1 ");
       Serial.println(blinkRate);
       value = 0; // reset val to 0 ready for the next sequence of digits
    }
  } 
}
//////////////////////////////////////////////////////////
void getNum2() {
   if( Serial.available())
  {
    char ch2 = Serial.read();
    if( isDigit(ch2) )// is this an ascii digit between 0 and 9?
    {
       value2 = (value2 * 10) + (ch2 - '0'); // yes, accumulate the value
    }
    else if (ch2 == 10)  // is the character the newline character?
    {
       blinkRate2 = value2;  // set blinkrate to the accumulated value
       Serial.print("here is your blinkrate 2 ");
       Serial.println(blinkRate2);
       value2 = 0; // reset val to 0 ready for the next sequence of digits
    }
  } 
}

What have you the line ending set to in the Serial monitor ?

As an aside, what you are setting are variables, not constants, but that is not the cause of the problem, merely a misuse of the word constant in this context.

Firstly I would recommend to avoid using delay() as it can cause a variety of issues.

If you only want to read numbers from the serial input you could look at the Serial.parseInt
There is a tutorial here: http://arduino.cc/en/Tutorial/ReadASCIIString

And also in your serial monitor ensure that on the drop down menu of it you have newline selected.

It would be best if your sketch prompted for the value it wanted you to input, and then continued reading and buffering your response until that response is complete (for example if you decide that your input line will be terminated by a newline then you can use that to determine when the complete input has been received. Once received, parsed and validated your sketch can inform the user whether the value was successfully set and offer them a chance to try again, or ask them for the next value.

Matt123:

/*
  • SerialReceive sketch
  • Blink the LED at a rate proportional to the received digit value
    */
    const int ledPin = 13; // pin the LED is connected to
    const int ledPin2=11;
    int   blinkRate=0;     // blink rate stored in this variable
    int   blinkRate2=0;
    int value;
    int value2;
    ////////////////////////////////////////////
    void setup()
    {
     Serial.begin(9600); // Initialize serial port to send and receive at 9600 baud
     pinMode(ledPin, OUTPUT); // set this pin as output
    }

////////////////////////////////////////////
void loop()
{
 getNum();
 getNum2();
 blink();
 blink2();
}
//...
/////////////////////////////////////////////////////////////////
void getNum() {
  if( Serial.available())
 {
   char ch = Serial.read();
   if( isDigit(ch) )// is this an ascii digit between 0 and 9?
   {
      value = (value * 10) + (ch - '0'); // yes, accumulate the value
   }
   else if (ch == 10)  // is the character the newline character?
   {
      blinkRate = value;  // set blinkrate to the accumulated value
      Serial.print("here is your blinkrate 1 ");
      Serial.println(blinkRate);
      value = 0; // reset val to 0 ready for the next sequence of digits
   }
 }
}

Note, I'm using Arduino 1.0.5

The actual value code looks correct. Tip: I use Visual Studio (or Visual C++ Express) for debugging C++ code. Any C++ compiler (with a good debugger) will help you visualize execution better.

First, every loop you check for input. If you want to "use for the rest of the program" I'd suggest putting it in setup().

Second, you alternate your reading of serial input by only reading 1 character at a time. Example:

// user inputs: 123\n456
// value  = 1 3  4 6
// value2 = 2 \n 5

I'm guessing you want to "accumulate" the input.

You need to block execution of code while waiting for input. delay() is useful for this case:

http://arduino.cc/en/Reference/Delay:
Certain things do go on while the delay() function is controlling the Atmega chip however, because the delay function does not disable interrupts. Serial communication that appears at the RX pin is recorded, PWM (analogWrite) values and pin states are maintained, and interrupts will work as they should.

However, you can also use the "Serial.readBytesUntil" function (with a good Serial.setTimeout) which will handle the delay (timeout) for you.

Here's some psuedocode to get you on the right path:

setup()
{
// serial & pin configuration
getnum1();
//getnum2();
}

loop()
{
//blink();
//blink2();
}

void getnum1()
{
    Serial.setTimeout(1000); // timeout every 1 second
    if (Serial.available())
    {
        bool waitingForInput = true;
        while (waitingForInput) // do this until input is received (not sure how safe this is)
		{
			delay(1); // for stability inside while loop
			
			int readLength = 10; // this should match buffer[10]
			char buffer[10];
			
			int length = Serial.readBytesUntil('\n', buffer, readLength); // read until newline, 10 characters, or timeout.  Fill buffer
			
			if (length > 0) // we have data!
			{
				for (int i = 0; i < length; i++)
				{
					if (buffer[i] == '\n') // newline! we're done!
					{
						waitingForInput = false; // sweet!
						break; // break the for loop, which will jump us past the `else` statement and out of the while loop
					}
					else
					{
						// fill your value, current character is buffer[i]
					}
				}
			}
			else // we do not have data!
			{
				Serial.write("Please input a number for value/motor 1 followed by a newline"); // outputs every timeout (
			}
		} // end of while(waitingForInput)
    } // end of if (Serial.available)
} // end of getnum1

References:
ReadBytesUntil
SetTimeout

Edit Pastebin of getnum1() for syntax highlighting

Hope that helps!
Nathan

Hey guys thanks for your suggestions. I made some progress using the ReadASCIIString tutorial, I have the code written below with the intent of just taking in 3 values separated by commas, and making them into global vars. What I do need help with is making the x,y,z vars usable in other functions, right now I don't think they are in the scope of anything other than getNum(). Would you suggest I use pointers to modify them directly? Or have an individual functions return each x,y, and z and set those functions to another variable for usage? Also, Nathan thanks for the great response. My question, is using the Serial.parseInt basically the same as "accumulating input"? Also, any alternatives to visual studio for mac? Thanks.

void setup(){
  Serial.begin(9600);
  Serial.println("type 3 csv");
}

void loop(){
  
   getNum();

}

void getNum(){
  while(Serial.available()>0){
    int x=Serial.parseInt();
    int y=Serial.parseInt();
    int z=Serial.parseInt();
    
    if (Serial.read()=='\n'){
      Serial.print("x is: ");
      Serial.println(x);
      Serial.print("y is: ");
      Serial.println(y);
      Serial.print("z is: ");
      Serial.println(z);
     
      Serial.println("thanks done");
    }
  }
}

Servo code, but might be of use for your motor control code.

//zoomkat 11-22-12 simple delimited ',' string parse 
//from serial port input (via serial monitor)
//and print result out serial port
//multi servos added 

String readString;
#include <Servo.h> 
Servo myservoa, myservob, myservoc, myservod;  // create servo object to control a servo 

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

  //myservoa.writeMicroseconds(1500); //set initial servo position if desired

  myservoa.attach(6);  //the pin for the servoa control
  myservob.attach(7);  //the pin for the servob control
  myservoc.attach(8);  //the pin for the servoc control
  myservod.attach(9);  //the pin for the servod control 
  Serial.println("multi-servo-delimit-test-dual-input-11-22-12"); // so I can keep track of what is loaded
}

void loop() {

  //expect single strings like 700a, or 1500c, or 2000d,
  //or like 30c, or 90a, or 180d,
  //or combined like 30c,180b,70a,120d,

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      if (readString.length() >1) {
        Serial.println(readString); //prints string to serial port out

        int n = readString.toInt();  //convert readString into a number

        // auto select appropriate value, copied from someone elses code.
        if(n >= 500)
        {
          Serial.print("writing Microseconds: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.writeMicroseconds(n);
          if(readString.indexOf('b') >0) myservob.writeMicroseconds(n);
          if(readString.indexOf('c') >0) myservoc.writeMicroseconds(n);
          if(readString.indexOf('d') >0) myservod.writeMicroseconds(n);
        }
        else
        {   
          Serial.print("writing Angle: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.write(n);
          if(readString.indexOf('b') >0) myservob.write(n);
          if(readString.indexOf('c') >0) myservoc.write(n);
          if(readString.indexOf('d') >0) myservod.write(n);
        }
         readString=""; //clears variable for new input
      }
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

What I do need help with is making the x,y,z vars usable in other functions, right now I don't think they are in the scope of anything other than getNum()

So declare them as global (at the start of the program) rather than local to the function

Thank you for the guidance, I pretty much have everything I need. :slight_smile:

Matt123:
Also, Nathan thanks for the great response. My question, is using the Serial.parseInt basically the same as "accumulating input"? Also, any alternatives to visual studio for mac? Thanks.

parseInt looks for an integer, the reference page is very vague and ambiguous, you might need to test it.

Mac uses Xcode, been a while since I used it, but it should have C++.

Finally, in your code, getnum is inside "loop()" This means, I can set x,y,z every time loop executes. (Meaning I can change x,y,z on a whim) This might or might not be intended functionality. From what I understood, it is not intended.