Arduino serial to variable

Hi all,

i have a visual basic program send serial to arduino. Now i would like my arduino to perform a action based on the received serial.

if i send irnec:1234567890, then my arduino needs to send an nec command 1234567890. (Sending ir commands is all working just need a way to initialize this using serial).

In short i need to combine serial communication into 1 variable. How do i do this?

Regards,

In the Arduino sketch, you need to create a character array to store the incoming data in. It needs to be large enough to hold the longest string you will send the Arduino.

#define arraySize 32
char inData[arraySize];

Or whatever size you need…

Then, you need an index into this array. Because the size of the array is necessarily small, a short should be big enough:

short index = 0;

Then, as you receive data, store the byte in the array, and increment the index:

char inChar;
if(Serial.available() > 0)
{
    while(Serial.available() > 0 && index < arraySize-1)
   {
       inChar = Serial.read();
       inData[index] = inChar;
       index++;
       inData[index] = '\0'; // Add a null at the end
   }
}

// Now, inData contains all the data read. So whatever

There are other ways to do this. They may be better, depending on what data is being streamed across.

Whilst the above code is good and all, there are four things I should point out:

  • sizeof(short) = 16 (this is not a C datatype, but it is a C++ one), and sizeof(int) = 16. You can test this out in your own. What this means is that both variables will take the same amount of RAM. Given that the serial library declares the size of the storage to be 128 bytes, including <inttypes.h> and using uint8_t will provide you a range from 0000000b to 11111111b (0dec to 255dec) as your index. Note that this is the same as a char datatype. If you are using C, you can perform arithmetic operations in your char variable (‘a’ + 1 = ‘b’, and so on). C++ has stronger typing, so I do not know what will happen. Careful not to overflow it or you will reset back to 0.
  • The code in question does not account for backspaces. It will happily store a backspace in the array and add one. To fix, you should make a case for when the received character is 8 (BS), print a ‘\b’ on your terminal (if you are using one), and move index back by 1 if and only if it is more than 0. If index is 0 and you move it back by 1, you will reset index to 255 because of the way addition/subtraction are performed in memory.
  • The code does nothing with the data after it is sent. Whilst the while loop is good to perform a clear in the serial buffer, the same can be done with the standard loop() function that many people use in their sketch, thus you can rid of the while loop. You should also check and see if the received character is 13 (CR). This is the enter key in your keyboard, that way you can commit your data and have the MCU process it.
  • Unless you are printing the string with the standard print() functions provided by any libraries at some point, you don’t need to add a character 0 (NULL) at the end. You know the length of the string from index, you can use that to manipulate it. Only add NULL if you are going to be using functions from <string.h>. If you add NULL remember that you can only accept 254 characters at max, character 255 must be the NULL.

Well, rats. There I was thinking byte when when my fingers pecked out short.

Handling of backspace should be done on the VB end before the data is sent, so that really shouldn't be an issue.

The code does nothing with the data sent, because what to do with it was not defined.

The terminating NULL is needed if the resulting collection of characters is ever to be passed to any function expecting a string, such as printf, atoi, strcmp, strtok, etc.

The datatype thing, it happens to me all the time.

It really depends though how the data is sent. I usually send data thru a serial terminal “on the fly”, so I process all the char data in the ATmega. Now, if the application in question then sends all the preformatted data in a burst, then your code is fine. It should be noted that the application in question should impose the character limit sent in a line. If you are sending data in bursts, judging from the way the serial library is written, you might be able to send the trailing NULL with it.

True on the functions expecting strings as arguments though.

Thank you for your comments, It helped me a lot.

It's a fresh new day so I will get to this right away!

Thank you,

Ok, I got it working but now im at the next step…

If i receive irnec:123456789, i want to seperate this to irnec and 123456789. Then i want to compare irnec in a if.

Let me clarify:

Arduino receives: irnec:123456789
Arduino splits this in: irnec & 123456789
Arduino compares irnec, if it matches then: send nec(123456789)

I got to the part were i got irnec:123456789 in 1 array, this my code (ignore the dutch comments ;)):

    while(Serial.available() > 0 && index < arraySize-1)
    {

      // De binnengekomen data opslaan in inChar.
      inChar = Serial.read();

      // De binnen gekomen data toewijzen aan de array.
      inData[index] = inChar;

      // Index optellen met 1.
      index++;

      if (index == 15) {
        
        // Do something.
        
        index = 0;
        inData[0] = '\0';
      }

      // De array beeindigen.
      inData[index] = '\0'; // Add a null at the end
    }

Thanks in advance.

Regards,

I mentioned in reply #1 that there were better ways to do things if the incoming data was consistently formatted. If all the incoming data is in the form sssss:nnnnnnnnn, then you should have different processing depending on whether you’ve seen the colon, or not.

Store the inChar value in the array, as you are doing now, if the character is not a ‘:’. If it is a colon, break out of the while loop that is currently reading data, and start another while loop to read the number portion.

char inChar;
char inData[16]; // Arrays should be even sizes
byte index = 0;
byte inDigit;
long inNumber = 0;
while(Serial.available() > 0)
{
    inChar = Serial.read();
    if(inChar != ':' && index < 15)
    {
       inData[index] = inChar;
       index++;
       inData[index] = '\0';
    }
    else
       break;
}

if(inChar == ':')  // Now, read the number
{
   index = 0;
   while(Serial.available() > 0 && index < 10)
   {
       inDigit = Serial.read() - '0'; Convert '3' to 3, for instance
       inNumber *= 10; // Multiply existing number by 10
       inNumber += inDigit; // Add the new digit

       index++; // Don't try to read too many digits
   }
}

// Now, inData contains irnec as a string and 
// inNumber contains 123456789 as a number

Thank you very much. That did the trick :D ;D

Regards,

Joey

Hi there again,

The code you send worked great, but when I start modifying this code it starts to fail.

The plan is if arduino receives irnec:1234567890, and ir nec code will be send (sending the code is no problem, but what to send en when I want it is the problem).

I tried to keep to code flexible for later modifications (like ir rc5 instead of nec).

Would you look at the following code?

char inChar;
char inData[16];
byte index = 0;
byte inDigit;
long inNumber = 0;

int iniatilizeCommand = 0;
int TypeCommand = 0;


void setup()   
{ 

  Serial.begin(9600);

}


void loop()                     
{

  if (iniatilizeCommand == 0) {

    while(Serial.available() > 0) {
      
      inChar = Serial.read();
      
      if(inChar != ':' && index < 15) {
        
        inData[index] = inChar;
        
        index++;
        
        inData[index] = '\0';

      } else {
        
        break;
        
      }
      
    }

    if(inChar == ':') {
      
      index = 0;
      
      while(Serial.available() > 0 && index < 10) {
 
        inDigit = Serial.read() - '0';
  
        inNumber *= 10;
    
        inNumber += inDigit;

        index++;
      
        iniatilizeCommand = 1;
      }
      
    }
    
  } else {

    CheckTypeCommand(inData);

    PerformCommand(inNumber);
    
  }
  
}



void CheckTypeCommand(char Command[]) {


  if (strcmp(Command, "irnec") == 0) {

    TypeCommand = 1;
    
  }

}

void PerformCommand(int Commandint) {

  switch (TypeCommand) {
    case 0:
      break;
    case 1:
      Serial.print("Send IR NEC code:");
      Serial.println(Commandint);
      break;
    default: 
      break;
  }

  iniatilizeCommand = 0;
  inNumber = 0;
  inData[0] = '\0';
  index = 0;
  TypeCommand = 0;
  Commandint = 0;
}

My plan was:

  • arduino receives irnec:123456789 → Arduino sends IR Nec code (123456789).

You maybe see that i’m a novice, but I really want to pull this off.

Please help.

Regards,

Joey

but when I start modifying this code it starts to fail.

Starts failing in what way?

On the first pass through loop, you collect data in inData and inNumber, but you do nothing with them.

On the next pass, you call CheckTypeCommand and PerformCommand. If I was you, I'd modify CheckTypeCommand to return an int, and store that value in a local variable called TypeCommand, instead of using a global variable for that purpose.

I'd add Serial.print and Serial.println to validate that reasonable data was passed in to CheckTypeCommand and ProcessCommand.

Also, the Serial.print commands (print and println) take optional second arguments (DEC, BYTE, HEX, etc.) to assure that data is printed in the correct format. You might want to read up on them, and use them as required.

It totally failed, like no response...

But I will try again, thank you for your advice.

Regards,

Joey

Ok i’m done, but have 1 issue. I build in a debug, if debug = 1 → debug serial strings are sent. But when I change debug to 0 it fails.

Anybody care to help, i can’t find the problem.

#include <IRremote.h>

IRsend irsend;

// If you want debugging, set this value to 1.
int debug = 0;

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

void loop() { 

  checkSerial();

}

void checkSerial() {

  char inChar;
  char inData[16]; // Arrays should be even sizes
  byte index = 0;
  byte inDigit;
  long inNumber = 0;
  int TypeCommand = 0;

  while(Serial.available() > 0)
  {
    inChar = Serial.read();

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("CheckSerial() :: inChar = ");
      Serial.println(inChar); 
    }

    if(inChar != ':' && index < 15)
    {
      inData[index] = inChar;

      if (debug == 1) {
        Serial.println("----------------------------------------------------");
        Serial.print("CheckSerial() :: inData[");
        Serial.print(index,DEC);
        Serial.print("] = ");
        Serial.println(inData[index]);
      }

      index++;
      inData[index] = '\0';
    }
    else
      break;
  }

  if(inChar == ':')  // Now, read the number
  {

    index = 0;
    while(Serial.available() > 0 && index < 11)
    {
      inDigit = Serial.read() - '0'; //Convert '3' to 3, for instance
      inNumber *= 10; // Multiply existing number by 10
      inNumber += inDigit; // Add the new digit
      index++; // Don't try to read too many digits

      if (debug == 1) {
        Serial.println("----------------------------------------------------");
        Serial.print("CheckSerial() :: inDigit = ");
        Serial.println(inDigit, DEC);
        Serial.print("CheckSerial() :: inNumber = ");
        Serial.println(inNumber);
      }

    }

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.println("Result:");
      Serial.print("CheckSerial() :: inData[] = ");
      Serial.println(inData);
      Serial.print("CheckSerial() :: inNumber = ");
      Serial.println(inNumber);
    }

  }

  if (inNumber > 0) {

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.println("inNumber > 0");
      Serial.print("inNumber = ");
      Serial.println(inNumber);
    }

    TypeCommand = CheckTypeCommand(inData);

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("TypeCommand = ");
      Serial.println(TypeCommand);
    }

    PerformCommand(TypeCommand,inNumber);
  }

  // Now, inData contains irnec as a string and
  // inNumber contains 123456789 as a number 
}

int CheckTypeCommand(char inData[16]){

  int result = 0;

  if (strcmp(inData, "irnec") == 0) {

    result = 1;

  }

  if (strcmp(inData, "irrc5") == 0) {

    result = 2;

  }

  return result;

}

void PerformCommand(int TypeCommand, long Commandint) {

  if (debug == 1) {
    Serial.println("----------------------------------------------------");
    Serial.println("PerformCommand :: Function called with folowing variables:");
    Serial.print("TypeCommand = ");
    Serial.println(TypeCommand);
    Serial.print("Commandint = ");
    Serial.println(Commandint);
  }

  switch (TypeCommand) {
  case 0:
    break;

  case 1:

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("PerformCommand :: Switch resulted in Case 1 :: Sending IR NEC Code = ");
      Serial.println(Commandint);
    }

    sendIRCode(0,Commandint);

    break;

  case 2:

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("PerformCommand :: Switch resulted in Case 2 :: Sending IR RC5 Code = ");
      Serial.println(Commandint);
    }
    
    sendIRCode(1,Commandint);
    
    break;

  default:
    break;
  }

}

void sendIRCode(int codeType, long sendedCode) {
  
  int toggle = 0;

  if (debug == 1) {
    Serial.println("----------------------------------------------------");
    Serial.println("sendIRCode :: Function started");
  }

  switch (codeType) {

    // If codeType == 0, set send code in NEC
  case 0:

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.println("sendIRCode :: Switch resulted in Case 0 :: Sending mode to NEC");
    }

    irsend.sendNEC(sendedCode, 32);

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("sendIRCode :: Sending IR NEC code : ");
      Serial.print(sendedCode);
      Serial.println(" has been sent.");
    }

    break;

    // If codeType == 1, set send code in RC5
  case 1:

    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.println("sendIRCode :: Switch resulted in Case 1 :: Sending mode to RC5");
      Serial.print("sendIRCODE :: Preparing to send IR RC5 code : ");
      Serial.println(sendedCode);
    }

    toggle = 1 - toggle;
    
    // Put the toggle bit into the code to send
    sendedCode = sendedCode & ~(1 << (12 - 1));
    sendedCode = sendedCode | (toggle << (12 - 1));
    irsend.sendRC5(sendedCode, 12);
    
    if (debug == 1) {
      Serial.println("----------------------------------------------------");
      Serial.print("sendIRCode :: Sending IR RC5 code : ");
      Serial.print(sendedCode);
      Serial.println(" has been sent.");
    }

    break;
    
  }
}

For example my debug strings are:

----------------------------------------------------
CheckSerial() :: inChar = i
----------------------------------------------------
CheckSerial() :: inData[0] = i
----------------------------------------------------
CheckSerial() :: inChar = r
----------------------------------------------------
CheckSerial() :: inData[1] = r
----------------------------------------------------
CheckSerial() :: inChar = r
----------------------------------------------------
CheckSerial() :: inData[2] = r
----------------------------------------------------
CheckSerial() :: inChar = c
----------------------------------------------------
CheckSerial() :: inData[3] = c
----------------------------------------------------
CheckSerial() :: inChar = 5
----------------------------------------------------
CheckSerial() :: inData[4] = 5
----------------------------------------------------
CheckSerial() :: inChar = :
----------------------------------------------------
CheckSerial() :: inDigit = 1
CheckSerial() :: inNumber = 1
----------------------------------------------------
Result:
CheckSerial() :: inData[] = irrc5
CheckSerial() :: inNumber = 1
----------------------------------------------------
inNumber > 0
inNumber = 1
----------------------------------------------------
TypeCommand = 2
----------------------------------------------------
PerformCommand :: Function called with folowing variables:
TypeCommand = 2
Commandint = 1
----------------------------------------------------
PerformCommand :: Switch resulted in Case 2 :: Sending IR RC5 Code = 1
----------------------------------------------------
sendIRCode :: Function started
----------------------------------------------------
sendIRCode :: Switch resulted in Case 1 :: Sending mode to RC5
sendIRCODE :: Preparing to send IR RC5 code : 1
----------------------------------------------------
sendIRCode :: Sending IR RC5 code : 2049 has been sent.

I send: irrc5:1 (The last line where IR RC5 suddenly changes from 1 to 2049 is correct, this is normal.)

Thank you in advance.

Regards,

Joey
[edit] I recuded the problem, without debugging inNumber isnt greater then 0, which it should be.[/edit]
[edit] I added a short delay(5) after the inData processing, and it seams this solved the problem. Why?[/edit]