Serial Commands

Hi I am trying a touchscreen to control a machine that does a repetitive task. I am trying to get it set up so that I can type in the number of cycles I want the machine to run and then have the machine run that many times.

I am doing this using serial commands so for the touchscreen side I have it as Serial.print(number); where number is an int that is set when a number is hit on the touch screen. It is set up so that you can type in any number up to 1000 so n - 0:999.

My problem is that when I do the serial send it sends the value in ASCII so if I hit 3 on the touch screen and I do cycles = Serial.read(); the value of cycles is 51. I can work with this by subtracting 48 from the incomming number, but this will only work for values 0-9, I cant send a 12 or something. How can I do this? I have attached a sample code that shows how I am using the serial command on the arduino side.
Thanks!

int ledPin = 3; // LED connected to digital pin 13
int n=0;
// The setup() method runs once, when the sketch starts

void setup() {
// initialize the digital pin as an output:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}

void loop()
{
if( Serial.available() > 0){
n = Serial.read();
if(n>0){
Serial.println(n);
for(int b=0; b<n-48; b++){

digitalWrite(ledPin, HIGH); // set the LED on
delay(500); // wait for a second
digitalWrite(ledPin, LOW); // set the LED off
delay(300); // wait for a second
}
}
}
}

I cant send a 12 or something. How can I do this?

Well, “12” will be sent as 0x31 (49 decimal) and then 0x32 (50 decimal).
The first digit has a decimal weight of 101, and the second a decimal weight of 100.
You’ve already correctly worked out worked how to do the 100 case…

Here is one way (not the best way):

char inData[10];
int index;

void loop()
{
   index = 0;
   inData[index] = '\0';

   while(Serial.available() > 0)
   {
       inData[index] = Serial.read();
       index++;
       inData[index] = '\0';
   }

   if(index > 0)
   {
       int inVal = atoi(inData);
       // inVal is now the integer value that was sent
   }
}

The reason that this is not the best way is that is assumes that all the serial represents one integer value, that the data that represents one value is all available at the time the first character is available, and that all the characters are numeric representations. None of the assumptions is likely to always be true.

To deal with this, you need start and end markers for the data. Something like “<592>”. Then, you can search the serial data for the start of packet marker (<), and read the data until the end of packet marker (>) arrives. You can test, then, for a reasonable number of characters between the markers, and that all the characters represent numbers (i.e. not stray letters in between).

The approach allows you to deal with dropped data. For example, if the serial stream contains “12><439><56><1”, you can see where the whole packet is. Without the markers (“12439561”), the start and end of a number is impossible to pick out. If the stream looses a character or two (“12>39><56><1”, you can see that, and deal with the partial packet (by discarding it, most likely).

Serial.print can print in numerous forms. By default, it converts to ASCII, and most options indicate this also.

But, since you know you want to send two bytes for an int, and you want to read two bytes for an int… Just tell it to do that, and use shifting to move the bits into the right place when re-assembling.

Here’s an example…

On the sending side:

 int foo = 5000;
   // get the first byte of the int
 byte a = foo >> 8;
   // get the last byte of the int
 byte b = (byte) foo;

  // send two bytes as raw values
 Serial.print(a, BYTE);
 Serial.print(b, BYTE);

on the receiving side:

...

int foo = 0;

byte a;
byte b;

a = Serial.read();
...
b = Serial.read();

foo = ((int) a << 8) + b;

Edit: FWIW, it pays to read this page to understand how and why this works: http://arduino.cc/en/Reference/Bitshift

!c

Drone,
Thank you very much for the help. The code works, however I am having trouble understanding it. How do you know I want to send 2 bytes of data? does each byte correspond to a digit? Also I know the >>8 shifts the foo variable by 8 bits but what does this actually accomplish, you send it send the first byte why? and what does (byte) foo do? and then a little more detailed description on the receiving end would be very helpful too.
Thank you and sorry for the questions, I would just like to understand it better.

removed for my ignorance, sorry.

@MatthewS, no that isn’t the way it is.
“<<8” multiplies by 256, “>>8” divides by 256.
Nothing to do with “sending”.

Shifting by 1 multiplies or divides by 2 (depending on direction), so shifting by “n” multiplies or divides by 2n

Multiplying by 256 has the useful property that a byte value of 0x0001 becomes 0x0100.

However, this isn’t a great deal of use when you’re trying to convert decimal values, when powers of ten work better than powers of 2.

How do you know I want to send 2 bytes of data?

To send two decimal digits, you have to send (at least) two bytes.
Suppose you want to send the decimal value “42”.
The first byte sent will be 0x34, and the second 0x32.
Subtract 0x30 from each byte and you’re left with 0x04 and 0x02.
Multiply the first by 10 and add the second and put the result in at least a byte, preferably an int.
(0x04 * 10) + 0x02 = 42 = 0x2A

dirtbiker,

The problem I see is that you’re talking between two arduinos. You’re not talking to a serial client application that has a human reading the output, and therefore there is no need to go through a human-readable transitional state. Thus, we don’t need to use up to 5 bytes to transmit two bytes of data, and we don’t need atoi() to convert it back to an integer on the receiving end - which is heavy and expensive. What happens when you want an unsigned int? atou(). What happens when you want a float? strtod() ← yeah have fun w/ that one!

You want to send two bytes, because an integer is two bytes in AVR GCC. (That’s how I knew you wanted to send two bytes wink)

It’s very simple - an integer is 16 bits, or two bytes, a byte is 8 bits, or one byte.

You can send raw bytes via serial, so to transmit an integer as two bytes, simply send the two bytes that make up the integer as they are.

Yes, bit shifting is a way to do multiplication/division. But that’s a pretty complex answer (except to explain why the receiving end works the way it does =) to “send the first byte and then the second byte of the integer.”

Without using the phrase “bitwise math”, let me explain very simply what I did:

1: break an (int) into its two component (byte)s:

byte a = foo >> 8;

In this case, I am shifting the left 8 bits of the int to be the right 8 bits, basically, removing the LSB of the int. There’s some type conversion magic here that solves one problem for me, which I’ll explain in the next line:

byte b = (byte) foo;

This is taking advantage of the type conversion “magic” – when I convert an (int) [16 bits] to a (byte) [8 bits] like this, it takes the LSB of the int. That is, the “last” 8 bits.

In the previous line, I relied on this same type conversion behavior – by moving the ‘first’ (MSB) 8 bits to the right, thus replacing the LSB value before the type conversion kicked in.

So, if I were to look at the bits that make up things – the shifting for ‘a’ makes the (int) look like these bits (assuming that all bits in the int were ‘1’) :

0000000011111111

(n.b.: this actually only works if all values are 1 if it’s an unsigned int - a signed int will create problems, you won’t see any if you stick between the value 0 and 1000 as you specified, but I highly suggest changing every instance of ‘int’ to ‘unsigned int’ if you don’t intend to send negative values.)

So now, you have ‘a’ and ‘b’ each holding one byte of the int. You can transmit them as raw bytes using:

Serial.print(a, BYTE); 
Serial.print(b, BYTE);

the BYTE constant tells it to not attempt to convert the value to ascii.

As to why I send the MSB as the first byte? It makes this more obvious: g

foo = ((int) a << 8) + b;

Which, again, results in some type conversion fun…

First, I say the (byte) a is now going to be an (int). Just like the other conversion takes the last 8 bits, this conversion makes my byte the last 8 bits of the new integer. So, I shift them 8 bits to the left, which is the exact opposite of what I did on the sending side. (A simple analogy: what’s the easiest way to explain how to undo something you did? Answer: Do the exact opposite of what you did. =)

int foo = ((int) a << 8) + b;

Is just a lazy way to say:

int foo = ((int) a << 8 ) | (int) b;

And, ‘foo’ now holds your re-constructed integer.

So, basically, we’re looking at shifting not as an arithmetic operation, but a way of isolating certain values. Since you’re only dealing with values of 0-1000, this works perfectly fine. If you intend to use values much higher (greater than (2^16)/2) - I suggest you typecast your int as a unsigned int to avoid problems with how shifting handles the sign bit in signed integers.

Either way, if you’re just talking about sending data between two arduinos, you can use this technique to send from one to four byte values (e.g. unsigned long) very efficiently and easily.

Take a look at the following:

void serial_write( unsigned int val ) {
  
    Serial.print( val >> 8, BYTE );
    Serial.print(val, BYTE);
  
}

void serial_write( unsigned long val ) {
  
    Serial.print( val >> 24, BYTE );
    Serial.print( val >> 16, BYTE );
    Serial.print( val >> 8, BYTE );
    Serial.print(val, BYTE);
  
}

Very simple, no?

Re-assembling one of those unsigned longs on the other end, you ask?

                value  = (unsigned long) input_serial_buffer[0] << 24;
                value |= (unsigned long) input_serial_buffer[1] << 16;
                value |= (unsigned long) input_serial_buffer[2] << 8;
                value |= (unsigned long) input_serial_buffer[3];

!c

It's a waste of operations!

...right up to the point when things get out of sync and you can't decide which of the bytes is which (most or least significant) and you need to put a delimiter in there... 8-)

...right up to the point when things get out of sync and you can't decide which of the bytes is which (most or least significant) and you need to put a delimiter in there... 8-)

Which is why I always use MSB first, and wrap it up in functions grin.

!c

In a continuous stream, how do you tell which is "first"?

In a continuous stream, how do you tell which is "first"?

Let me answer that question by asking the corollary: in a continuous stream of ASCII representations of integral values, how do you tell which is "first"?

How does it make a difference to that question, whether the data is ASCII or binary?

Can you tell better from the following, whether I meant "11" and "111" or "111" and "11"?:

1,1,1,1,1

The answer is: it's all about protocol. We're talking about how to send a singular value, not how to establish a communication protocol. ASCII conversion of data and atoi() usage have NO advantages over binary transmission absent a protocol which gives them an advantage.

FWIW, in my usage, I define a command/response protocol which makes it easy to ask the question "is this mid-stream, beginning, or end?"

!c

in a continuous stream of ASCII representations of integral values, how do you tell which is "first"?

Oh, that's easy - that's what delimeters are for.

How does it make a difference to that question, whether the data is ASCII or binary?

Because if it's binary, you can't have a non-numeric delimeter, the best you can hope for is some sort of break value.

Sure, using ASCII, you lose half your bandwidth, but that's the price you've got to pay sometimes.

A return question: how does your scheme work with the serial monitor?

A return question: how does your scheme work with the serial monitor?

It's not designed to work with the serial monitor, hence why I stated earlier:

The problem I see is that you're talking between two arduinos. You're not talking to a serial client application that has a human reading the output, and therefore there is no need to go through a human-readable transitional state. Thus, we don't need to use up to 5 bytes to transmit two bytes of data, and we don't need atoi() to convert it back to an integer on the receiving end

We can talk protocols all day. We could talk about how TCP works for reliable transmission. Does that help everyone involved? I, personally, hate the idea of coming in mid-stream to a communication protocol and expecting it to "just work". Generally, I don't send data between two devices unless there's a good reason to - and I like to know if my target audience got it, hence a sequence of events occurs...

A: "Are you there?"

B: "Yes, I am.""

A: "I want you to have this, and this is exactly x bytes."

A: this

B: "I received this, not only was it x bytes, it was also in a value range which I found acceptable for things of this type."

Edit: it's worth noting that I chose the answer I did originally, because nothing in the OP's post talked about any of: reliability, mid-stream interception, recoverability, or guaranteed reception of data. Neither you nor I know what his second device is doing with the data, and whether it's important that corrupt entries be re-transmitted or not. I simply offered an alternative way that might or might not be easier for his problem - given that none of us know exactly what he's trying to achieve outside of sending an integer. While this talk is all well-and-good, you've made presumptions about his data transmission (it's ok to come in mid-stream) and use that to support your position. That's fine, but that's just another way to implement a protocol. I don't think you're incorrect, I'm just not sure its relevant to his problem. FWIW, by using a fixed state-transition and validation in my protocol, I have yet to encounter an error that wouldn't have also impacted an ASCII protocol (such errors being transmission errors) - and I generally use inter-device communication for "meaningful" data - meaning each part of the data is important, so I tend not to presume that coming in mid-stream in inter-device communication is a recoverable error without two-way communication.

!c