Getting Serial Value to remain constant

Greetings everyone! I finally decided to sign up and make use of all of your knowledge for my own help. What I need is some input on whether or not there is a way to essentially halt serial monitor or what would be the most logical way of doing so for my program.

To give you an idea of what I am trying to do, I am creating a pan function that will drive a motor either left or right. I am able to drive it with no problems as this isn’t my first rodeo. My trouble is, I will be getting 2 bytes of data sent serially from, in this case, one Arduino to another. I then convert on the receiver side from ASCII to an integer and move the motor positioning to an integer value position with the help of mapping a potentiometer.

int panning = 13;
int panSpeed = 11;
int potValue = A15;
int panBrake = 8;


/***VARIABLES***/
int newVal, currentVal, tempPos;
int serialValue, result;
byte byte1;
char S_input[2];

/***FUNCTIONS***/
void left();
void right();
void brakeOn();
void brakeOff();
void pan();
void setNewValue();
void setCurrentValue(int);
int getNewValue();
int getCurrentValue();
int serialRead(int);


/*********************************************************/
void setup()
{  
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(potValue, INPUT);
  pinMode(panning, OUTPUT);
  pinMode(panBrake, OUTPUT);
}


void loop()
{
 if(Serial1.available()>0)
  {
   int  x, y;
   x = setNewValue();
    if(Serial1.available()) 
    {
      y = setNewValue();
      Serial.println(x+y);
      delay(1000);
    }
  }
  tempPos = analogRead(potValue);
  setCurrentValue(tempPos);
  pan();
};

/*********************************************************/

void pan()
{ 
 if(getNewValue() < getCurrentValue())
 {
   brakeOff();
   right();
 }
 if(getNewValue() > getCurrentValue())
 {
   brakeOff();
   left();
 }
 else if(getNewValue() == getCurrentValue())
 {
   brakeOn();
 }
}

void setNewValue()
{  
  S_input[] = x;
   delay(5);
   int i=0;      
   while(i<3)
   {
     S_input[i] = Serial1.read();
     i++;
   }
   Serial1.flush();
   result = atoi(S_input);
}


int getNewValue()
{
  return result;
}

void setCurrentValue(int x)
{
  currentVal = x;
  currentVal = map(currentVal, 356, 675, 0, 79);
}

int getCurrentValue()
{
  return currentVal; 
}

/**********PANNING**********/
void left()
{
  digitalWrite(panning, HIGH);
  analogWrite(panSpeed, 255); 
}

void right()
{
  digitalWrite(panning, LOW);
  analogWrite(panSpeed, 255); 
}

void brakeOn()
{
  digitalWrite(panBrake, HIGH);
  analogWrite(panSpeed, 0);
}

void brakeOff()
{
  digitalWrite(panBrake, LOW);
  analogWrite(panSpeed, 0);
}

Forcing an integer value into my “pan” function works 100%. My biggest problem lies either within the conversion from ASCII to integer, or with the serial monitor defaulting a variable to “0” because it hasn’t received any data from one transmission to the next (not sure if this actually happens). These transmissions are more than 30 seconds apart. Using a println statement with my ASCII conversion, I can see that it always prints “0” instead of the value that I send. I have tried numerous things, such as while loops within different functions including the main loop, creating const int variables, and ending the serial monitor, to just name a few, but still no success. I am hoping you bright, strapping individuals could take some time and see if there is a way that you would suggest doing this, and provide me with your input. If you need more information, please feel free to yell at me. By the time I figure this out, I will have no hair left on my head.

How is your data being sent? The process of sending the data, receiving it and converting it to an int, can be done easier than this.

Im assuming your sending the actual degree of rotation, so it looks like a regular int. But the receiving end, looks at it one int/char at a time.
So you send 123, you receive 1,2,3(int) or 49,50,51(char). You could also be sending HEX values AA, CD…FF, I don’t know.

What I suggest is to send a packet. Send your data within brackets, and have the receiving end remove and convert what’s in those brackets.
Example: <125>

Have your receiving code, look for the ‘<’, then look at the data that follows it, and store it in a char array. Keep storing the data until the code see ‘>’. This will let the code know it has all the data, and that it should now convert the char array to an int, using the atoi() function.

It may sound difficult but it is a good way to handle large amounts of data without anything being mixed up.

You call setNewValue, and the first thing you do there is to say S_input[] = x;. Two things... what position in the array are you trying to set to x, and do you realize that x is not in scope here?

I would also question your logic here...

 if(Serial1.available()>0)
  {
   int  x, y;
   x = setNewValue();
    if(Serial1.available()) 
    {
      y = setNewValue();
      Serial.println(x+y);
      delay(1000);
    }
  }
  tempPos = analogRead(potValue);
  setCurrentValue(tempPos);
  pan();
};

If your y value has not yet arrived, you will set x to the y value the next time though loop(). I don't know how likely this is, but it's just asking for trouble. If the serial data comes in fairly quickly, you might want to loop until you get both x and y values.

Are the x and y single digits?

Thanks for the quick replies! As of right now, this is the code for the other Arduino that is connected serially:

int output, output1;


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


void loop()
{
 if(Serial.available())
  {
   output = Serial.read();
   Serial1.write(output); 
  }
}

The actual hardware that I will be using will essentially be doing the same exact thing. It will be sending one byte at a time, which is why I have the x & y values storing those two bytes. What will be happening is it an integer value such as 45 for example will be sent serially, and will need to be converted back to an integer on the receiver end. When I first started doing this project, I would send 45 and it would print 45 but only recognize the lower byte. So by using the char array, I can store "4" and "5" and combine both bytes into a single integer of "45". Atleast that is what I thought I was doing in my code. I realize that I may have coded something wrong which is why I this is driving me so nuts.

You can still do that, but I would send a leading character, so your code does not convert the wrong data. Lets say you send 45,23,85, and something happens on the receiving end and now you only get 5,23,85, but with your code, it will see it as 52,38,5 null or 0. However if you send a leading char like * or #, then your data will know to only store the values that follow, if it see * or # first. It is known as a handshake, so both sending and receiving devices are getting the correct data.

Your code could be as simple as this,

You send #45

if (Serial1.read() == '#' ) // looks at the first byte and sees if it is #. If the statement is true, then get the next two bytes
{
   data[0] = Serial1.read(); // 4
   data[1] = Serial1.read(); // 5
 
  new_data = atoi(data); // 45  <-  atoi ( '4' , '5')
}

Okay, I get what you are saying. So now my question is this.... Say that I get the value received, what happens when that value is received and the serial monitor is available again but nothing is sent (essentially the receiver is idle). Will that cause the data (due to my get and set functions) to change to a zero before that pan function finishes? To explain my intention further, I will have my pan function loop, cross referencing the current position with the new desired position until they are equal. But that problem I was having was the function would run once then check the serial monitor for another number. If there wasn't another value received at that moment, it seemed to overwrite the original value. My position needs to be idle until a new value is received after it pans to a location. I'm hoping this makes sense lol.

Well if it doesn't receive anything then you can make "new_data" equal zero with an ELSE statement.

Are you using servos, steppers or dc motors? Servos will go the given value and stay in that position if there is nothing telling it to change. A stepper may be a little more difficult because if it will step so many times based on the number your telling it to. So if you keep sending the same number, it will keep stepping. What you could do then is get the value, store it and only step if the new value is different from the previous value.

It is actually a gear motor. There is a pin that goes through a potentiometer and as the gear spins the pot adjusts too, changing the value of the potentiometer. So pot value 0-1023 is mapped 10-89. As I mentioned, the pot value and position of the motor works perfect, it is the integer value of my variable "result" that is not working correctly after the conversion and the first loop through my pan function. I keep getting a "0" returned from the setNewValue function.

By the way, I forgot to mention, where you see "S_input = x;" , that is not part of my code. This is a snapshot from a few days ago and that part is not reflected in any current code. Everything else however is.

Ok your setNewValue function.

void setNewValue() // Set to void, so it should not be expected to return anything. I would change this to "int setNewValue()"
{
S_input = x; Since this would be the first value in S_input, shouldn’t it be S_input[0] = x ? Also x should equal something when it was declaired globally
delay(5);
int i=0;
while(i<3) // how many more values are you looking for?
{
S_input[ i ] = Serial1.read();
i++;
}
Serial1.flush(); // not really needed
result = atoi(S_input); // result should be returned
}

You actually beat me to my modification of my last post. This part is non-existant in my code… I pasted an old version I was playing around with.

S_input = x; // you should be inserting x with setNewValue(int x)

This part here is where I pull in the two bytes of my double digit values:

delay(5);
int i=0;
while(i<3) // how many more values are you looking for?
{
S_input[ i ] = Serial1.read();
i++;
}

Essentially sending “45” will receive “4” and “5” then are later combined into a single int of “45”. Since I will only be using 10-89 for my positions, I only need double digit numbers. So this is my new setNewValue function:

void setNewValue()
{
S_input = x;
delay(5);
int i=0;
while(i<3)
{
S_input = Serial1.read();

  • i++;*
  • }*
  • Serial1.flush();*
  • result = atoi(S_input);*
    }[/quote]

I updated my last post.

Essentially sending "45" will receive "4" and "5" then are later combined into a single int of "45". Since I will only be using 10-89 for my positions, I only need double digit numbers.

If this function is supposed to get both values, then why call it again for y? This function will keep looking for values, 3 to be exact, when all you need is just the one per call.
Your better off recoding your sketch with the example I gave you in reply #4.

Does this make a difference for what I am trying to accomplish?

result = atoi(S_input); // result should be returned

I have a getNewValue function that returns result.

MattSki, you say that part is no longer in your code, then you show us some current code, and it's still in there. You sould post your entire, current code if you expect anyone to have even a small chance of helping you.

Does this make a difference for what I am trying to accomplish?

What WILL make a difference is for you to think about your data types, and the logic of your Serial.read stuff. Please post the entire, current code, copied and pasted from your successfully compiled, uploaded and tested sketch.

Well that works too, but why make another function when you can just return it from the first function. If you want another function to just return the value, then it’s ok to do that. It will mean more typing and functions to keep track of, but its fine I guess.

Current code as of this afternoon… Its very similar and I even had the getNewValue function unused at that time.

int panning = 13;
int panSpeed = 11;
int potValue = A15;
int panBrake = 8;


/***VARIABLES***/
int newVal, currentVal, tempPos, result;
char S_input[2];

/***FUNCTIONS***/
void left();
void right();
void brakeOn();
void brakeOff();
void pan();
void setCurrentValue(int);
//int getNewValue();
int getCurrentValue();
int setNewValue();

/*********************************************************/
void setup()
{  
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(potValue, INPUT);
  pinMode(panning, OUTPUT);
  pinMode(panBrake, OUTPUT);
}


void loop()
{
 if(Serial1.available()>0)
  {
   int  x, y;
   x = setNewValue();
    if(Serial1.available()) 
    {
      y = setNewValue();
      Serial.println(x+y);
      delay(1000);
    }
  }
  tempPos = analogRead(potValue);
  setCurrentValue(tempPos);
  pan();
};

/*********************************************************/

void pan()
{ 
 Serial.println(result);
 if(result < getCurrentValue())
 {
   brakeOff();
   right();
 }
 if(result > getCurrentValue())
 {
   brakeOff();
   left();
 }
 else if(result == getCurrentValue())
 {
   brakeOn();
 }
}

int setNewValue()
{
   delay(5);
   int i=0;      
   while(i<2)
   {
     S_input[i] = Serial1.read();
     i++;
   }
   result = atoi(S_input);
   return result;
}

/*
int getNewValue()
{
  return result;
}
*/

void setCurrentValue(int x)
{
  currentVal = x;
  currentVal = map(currentVal, 356, 675, 0, 79);
}

int getCurrentValue()
{
  return currentVal; 
}

/**********PANNING**********/
void left()
{
  digitalWrite(panning, HIGH);
  analogWrite(panSpeed, 255); 
}

void right()
{
  digitalWrite(panning, LOW);
  analogWrite(panSpeed, 255); 
}

void brakeOn()
{
  digitalWrite(panBrake, HIGH);
  analogWrite(panSpeed, 0);
}

void brakeOff()
{
  digitalWrite(panBrake, LOW);
  analogWrite(panSpeed, 0);
}

Well, is it current or is it "very similar"?

It doesn't compile.

int potValue = A15;
A15 not declared in this scope

Serial1 not declared. I assume you are using a Mega2560.

Now to the crux of the problem.

You check for Serial.available(), which will be true as soon as 1 byte comes in. You then call a function that reads two bytes. The two bytes might be there, or they might not. You should ALWAYS provide for the unexpected. ALWAYS send some fixed number of bytes, and wait for them to all come in, or use delimiters to indicate they are all in.

You then return setting x to the result, and check Serial.available again, with 1 or more bytes in the Serial buffer. Again, you can't be sure. Worse, if no y bytes are there when you check for available, you'll drop out the bottom of the if, and the next time through loop(), you'll set x to the y values if they have arrived in the meantime.

If, as you say, you are only going to be sending values between 10 and 89, why not explicitly send them as unsigned bytes:

byte x;
byte y;
x = 10;
y = 35;

Send your values this way, and on the other end, when you get Serial.available == true, read the byte, and (if you truely need them as ints),

return (int)result;

Chew on that for a bit, and consider that making assumptions can get you into really hard-to-find problems.

Some test code for two pot controlled servos that might be worth a look. The master code reads the pot values, maps the values to servo position values, adds a servo designator (a or b), and a comma (,) as the data string delimiter. Then the data string is sent to the arduino tx pin, which is connected to the slave arduino rx pin (the arduino grounds are connected). The slave arduino receives the data packet as a String, which then has the data numerical component converted to a servo control value. The data String is also evaluated for which servo (a or b) to which the control value is sent. The master code has a delay and deadband built in so there is not a data spew which is hard to read when viewing the serial monitor for returned values. I haven’t tested this particular setup, so it might work, or might not.

master code

//zoomkat dual pot/servo test 12-29-12
//view output using the serial monitor

#include <Servo.h> 
Servo myservo1;
Servo myservo2;

int potpin1 = 0;  //analog input pin A0
int potpin2 = 1;

int newval1, oldval1;
int newval2, oldval2;

void setup() 
{
  Serial.begin(9600);  
  myservo1.attach(2);  
  myservo2.attach(3);
  Serial.println("testing dual pot servo");  
}

void loop() 
{ 
  newval1 = analogRead(potpin1);           
  newval1 = map(newval1, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval1 > (oldval1+2)){  
    //myservo1.write(newval1);
    //Serial.print("1- ");
    //Serial.println(newval1);
    Serial.print(newval1);
    Serial.print("a,");
    oldval1=newval1;
  }

  newval2 = analogRead(potpin2);
  newval2 = map(newval2, 0, 1023, 0, 179);
  if (newval2 < (oldval2-2) || newval2 > (oldval2+2)){  
    //myservo2.write(newval2);
    //Serial.print("2- ");    
    //Serial.println(newval2);
    Serial.print(newval1);
    Serial.print("b,");
    oldval2=newval2;
  }
  delay(50);
}

slave 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
    }
  }
}

Yes, I am using a Mega2560. No problems in compilation.

You check for Serial.available(), which will be true as soon as 1 byte comes in. You then call a function that reads two bytes. The two bytes might be there, or they might not. You should ALWAYS provide for the unexpected. ALWAYS send some fixed number of bytes, and wait for them to all come in, or use delimiters to indicate they are all in.

I will explain what happens when running the code and serial monitors, maybe it will help? :~

I have both serial monitors open. Using my "current" code, if I type a "3" and press enter, I get nothing on the receive side. If I again type another "3" and hit enter, again, nothing. If I type "03" or any other double digit number, the receive side prints to its own serial monitor exactly as I sent it from the transmit serial monitor. So that only tells me that my code for reading the serial monitor is working only if I send the value as a double digit number,the receiver reads each digit of the double digit number as an individual byte.

OK, here’s a litt;e sketch for you to run. I didn’t want you to think that you absolutely HAD to use a byte value, but only that there needs to be a way to KNOW what you are receiving, and WHEN you have received enough to do the job.

Compile and load the following sketch. It shows one possible way of sending two values in the form of two characters for x and two characters for y.

int count = 0;
int  x,  y;
char strRcvd[5];

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

void loop() {
  if (Serial.available() > 0) {
    count++;
    if(Serial.available() >= 4)
    {
      for (int i=0;i<4;i++) {
        strRcvd[i] = Serial.read();
      }
      setNewValue(strRcvd);
      Serial.print(strRcvd);
      Serial.print("    ");
      Serial.println(count);
      count = 0;
    }
  }
}

void setNewValue(char *s) {
  //convert first two chars to make x
  //convert second two chars to make y
}

Now, fire up the serial monitor set line endings to “No line ending”, and send a line consisting of 4 numeric digits…

Sent: 1234
Received: 1234 367

Why did we get 367? Because every time through loop(), we check for at least 1 byte n the serial buffer. If we have 1, 2, or 3, or 4 bytes in the serial buffer, we increment count. So, we have gone through that first if statement 366 times before we had a full ‘packet’. Once we had 4 bytes, we read them in and called a function, then we showed the result.

Now try this:

Send: 456
Rcv: nothing
Send: 7
Rcv: 4567 (some random number)

Why? Because we have only sent 3 bytes, and we never trggered the >= 4 condition. Then, when we sent the 4, we had 4 bytes in the serial buffer and triggered the condition. The random number is still the number of times through loop(), but might be wrapped any number of times. Remember the AVR is EXCEEDINGLY fast, when compared with user input and 9600 baud.

Ignoring the count value, try sending the following, and note the result.

45678
1412883
45
345

Okay, I will play around with the above. I will also note that I took the advice of @HazardsMind and implemented the technique he provided, leading with an ASCII character, and so far, so good. I'm currently sitting next to everything all wired up and running. The only problem that I ran into was the read time was sometimes too fast for my input, so if I typed in a 45 it would read it, but if I immediately entered another number, it would only catch part of it. Had to throw in a short delay of 10 milliseconds, and all is well. The only thing I really need to do now is find a way to lead with a ASCII character on the output of the other device, since it will not be user input.