PC-Arduino serial communication - Page 1
Forum section: electronics / general

author: shebu18
author_rank: Frequent Contributor
timestamp: September 20, 2012, 09:01:57 am
content: Hello, i have managed to write to my arduino via a PC software made by me in visual basic. I now want to know how i can write 2 values between 0 and 255 to the arduino.It should be formatted like "motor1,motor2". after receiving this string by the arduino it shall put it into an array of 2. I am thinking something like:Code:if serial available>0count =0if serial.read="," then count++array=serial.readCould this work?

author: shebu18
author_rank: Frequent Contributor
timestamp: September 21, 2012, 02:51:16 pm
content: Nobody?If u send "U1234" from PC to the arduino i store the char string in an char array, like "U","1","2","3","4" .How do i convert these individuals to the number 1234 and "delete" the U?

author: firewalker
author_rank: Super Contributor
timestamp: September 21, 2012, 03:06:42 pm
content: Have a look at C functionsatoiandmemmoveof the string.h header.http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/http://www.cplusplus.com/reference/clibrary/cstring/memmove/Alexander.

author: Balaur
author_rank: Unknown
timestamp: September 21, 2012, 03:13:26 pm
content: RS232 it's a frame-oriented protocol, with either 7 or 8 bits per frame.Basically, there are two approaches:I) SerialisationAssuming you use 8 bit per frame, you can then directly send unsigned integers from 0 to 255. For that, you can use "Byte" vars in your VB program.For anything larger than that, i.e. 1234, you should use a data representation consisting in several bytes, ideally something that can be easily interpreted by the Arduino.Ex: the integer type on Arduino is two bytes. Then, you can send the two bytes through the serial link and use something like that:Code:tmp = Serial.read();tmp = tmp << 8;tmp |= Serial.read();II) ASCIIYou use printf to transform your value to a string on the PC, and "atoi" on the Arduino to get a value out of the string.This is an error-prone and terribly un-economic thing to do on an embedded platform. Please don't do that.Cheers,Dan

author: shebu18
author_rank: Frequent Contributor
timestamp: September 21, 2012, 03:34:14 pm
content: I wnat to format the string that i send from the PC to something like "U16483A4095X0Y1Z0"U, voltage, =16483A, current, =4095X, command 1, =0Y, command 2, =1Z, command 3, =0The arduino should now write in the the int variable for voltage the value of 16384, for current 4095 and the values of the commands in other variables. After this i set the voltage and so on.I managed to do the thing with 1 and 0, comparing it and light up a led o dimm it. That was one ascii character, not more.It is also ok if i do not interpret them as character, but their binary value, so 2 bytes for voltage, 2 for current, 1 for commands. So i send out 5 bytes of data.

author: Mechatrommer
author_rank: Super Contributor
timestamp: September 21, 2012, 06:59:16 pm
content: you may have trouble sending byte 0 and other special character for serial comm. your code looks not to work maybe something like this (my C and arduino is rusted already)...Code:byte data[2, MAXBUF];byte count = 0, ar = 0, buf;while (serial.available()) {buf = serial.read();if (buf == ',') {ar++;count = 0;} else {data[ar, count++] = buf;}}as for converting "U1234" to int 1234, you may send atoi et al instead of data or data (single and 2 dimension array respectively), you send it &data or &data[0,1]

author: andersm
author_rank: Super Contributor
timestamp: September 21, 2012, 07:59:29 pm
content: Something like this?Code:#include <ctype.h>enum variable{var_none,var_u = 'U',var_a = 'A',var_x = 'X',var_y = 'Y',var_z = 'Z'};unsigned int voltage;unsigned int current;unsigned int x;unsigned int y;unsigned int z;bool ReadData(void){static unsigned char currentVar = var_none;static unsigned int temp = 0;bool varUpdated = false;if (serial.available()){unsigned char serialInput = serial.read();if (isdigit(serialInput)){// Accumulate valuetemp *= 10;temp += serialInput;}else{// Assign previously read valueswitch (currentVar){case var_u:voltage = temp;temp = 0;varUpdated = true;break;case var_a:current = temp;temp = 0;varUpdated = true;break;case var_x:x = temp;temp = 0;varUpdated = true;break;case var_y:y = temp;temp = 0;varUpdated = true;break;case var_z:z = temp;temp = 0;varUpdated = true;break;case var_none:default:break;}// Remember variable we're readingswitch (serialInput){case var_u:case var_a:case var_x:case var_y:case var_z:currentVar = serialInput;break;default:currentVar = var_none;break;}}}return varUpdated;}Making it compile, work and so on is left as an excercise for the reader.

author: mariush
author_rank: Super Contributor
timestamp: September 21, 2012, 09:13:28 pm
content: I wouldn't bother with such complicated code ...The ascii codes are well known .. just open character map :0x30 = 0 , 0x39 = 9 A = 0x41 , U = 0x55 , X = 0x58 , Y = 0x59 , Z = 0x5ADefine an array of five integer (4 bytes variabiles) ... Initialize all with 0Initialize the current value you work with with 0 ( A = 0 , U=1 , X = 2 , Y = 3, Z = 4 in the array)array_position = 0; // a byte that holds the position in the array you work with.Loop through the bytes you receive from the serial..if byte = 0x41 then byte = 0x57;if byte = 0x55 then byte = 0x56; // now you have U , A, X , Y , Z in a row :: 0x56, 0x57, 0x58 , 0x59, 0x5A (VWXYZ in ascii table)if byte < 0x30 then byte = 0; // ignore anything under "0" digitif byte > 0x39 and byte < 0x56 then byte = 0; // ignore anything between digits and "V", which is our new code for U ... from V to Z we have our codes)if byte > 0x5A then byte = 0; // ignore anything after Zso now you have either u,a, x,y,z (well actually the characters V, W, X, Y, Z) or "0"-"9" ... or 0 which is invalid character for our programif byte !=0 then {if byte > 0x39 then // not a digit, so that means it's one of our codes ... change the array position we work with depending on the character herearray_position = byte - 0x56; // so position becomes a value between 0 and 4elsearray [ array_position] = array  * 10 + byte; // if it's the first digit ever, you have here array [ # ] = array [ # ] * 10 + digit ... that's why it's important to initialize the array values with 0.end if}read next byte from serialend loop(above is pseudocode, it's not supposed to be c or something like that)That's pretty much it, just "byte banging" - no need for atoi and other functions for something so simple. You can made it better by adding the lower case letters in the ifs to treat them as valid.The loop will just ignore anything that's not digit or uppercase u,a,x,y,z so you can use , and other characters to separate them.PS. and since it initializes all with 0, you can skip some of them if you want to, or write something like UA10X2YZ ... all would be 0 except A and X ... optionally, you can initialize the array with -1 or something like that and do a check after the serial reading is done and whatever remains -1 is not transferred to the main code.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 22, 2012, 01:59:52 am
content: Quote from: shebu18 on September 21, 2012, 03:34:14 pmParsing an ASCIIZ string like "U16483A4095X0Y1Z0" is a great excuse to use the much-hated sscanf() function. You can parse that particular string with just one line of code.For example:Code:#include <stdio.h>int main(void){int Voltage = 0,Current = 0,XCmd = 0,YCmd = 0,ZCmd = 0;char InputStr[] = "U16483A4095X0Y1Z0";const char * FormatStr = "U%dA%dX%dY%dZ%d";if (sscanf(InputStr, FormatStr, &Voltage, &Current, &XCmd, &YCmd, &ZCmd) == 5){ // Parsed all 5 parameters successfully...printf("Parse successful.\n""Voltage = %d\n""Current = %d\n""XCmd = %d\n""YCmd = %d\n""ZCmd = %d\n",Voltage, Current, XCmd, YCmd, ZCmd);}else{fprintf(stderr, "error: Parse failed\n");return 1;}return 0;}The AVR Lib C user manual has more information on sscanf(). You'd likely want to use sscanf_P(), which allows the format string be kept in flash memory.

author: shebu18
author_rank: Frequent Contributor
timestamp: September 22, 2012, 07:00:07 am
content: Thank you for your help but i will need to talk with someone from my city and let them explain to me the code. It is to much for me alone, i do not have that knowledge of C.I will try to make it simpler, i will send 5bytes. THe first 2 will be for voltage, the next 2 will be for current and the last will be for commands. This way i will put the first 2 bytes together, make a int and use it as voltage, i will do the same for current and in the last byte i will have 8 commands.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 22, 2012, 06:57:43 pm
content: If you can get the data into a binary format like that from the host then all you will need to do on the Arduino is something like this:Code:unsigned char GetByteFromSerial(){while (!Serial.available());return (unsigned char)Serial.read();}void GetDataFromHost(int & Voltage, int & Current, unsigned char & Commands){Voltage = (GetByteFromSerial() << 8) | GetByteFromSerial();Current = (GetByteFromSerial() << 8) | GetByteFromSerial();Commands = GetByteFromSerial();}void setup(){Serial.begin(19200);}void loop(){while (true){int Voltage;int Current;unsigned char Commands;GetDataFromHost(Voltage, Current, Commands);Serial.println("Data received from host...");Serial.print("Voltage: 0x");Serial.println(Voltage, HEX);Serial.print("Current: 0x");Serial.println(Current, HEX);Serial.print("Commands: 0b");Serial.println(Commands, BIN);}}

author: shebu18
author_rank: Frequent Contributor
timestamp: September 22, 2012, 07:34:37 pm
content: Thank you, that is exactly what i was searching for. I do not need more then voltage current and commands send from the user interface to the PSU.Another question, how can i know if, lets say the 3rd bit from the command byte is 0 or 1?

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 22, 2012, 08:11:03 pm
content: There are a couple of ways to do that.Here's one way:Code:unsigned char GetByteFromSerial(){while (!Serial.available());return (unsigned char)Serial.read();}void GetDataFromHost(int & Voltage, int & Current, unsigned char & Commands){Voltage = (GetByteFromSerial() << 8) | GetByteFromSerial();Current = (GetByteFromSerial() << 8) | GetByteFromSerial();Commands = GetByteFromSerial();}void setup(){Serial.begin(19200);}void loop(){while (true){int Voltage;int Current;unsigned char Commands;GetDataFromHost(Voltage, Current, Commands);Serial.println("Data received from host...");Serial.print("Voltage: 0x");Serial.println(Voltage, HEX);Serial.print("Current: 0x");Serial.println(Current, HEX);Serial.print("Commands: 0b");Serial.println(Commands, BIN);Serial.println();for (unsigned char BitPosition = 0; BitPosition < 8; ++BitPosition){Serial.print("Command Bit ");Serial.print(BitPosition);Serial.print(" = ");bool BitSetting = Commands & (1 << BitPosition);Serial.println(BitSetting);}}}You basically use the '&' operator to mask out the bit you want to inspect. If you want to check bit 0 (the least significant bit) then you say:Code:if (Commands & (1 << 0)){ // The bit is set. Act accordingly...}Note that the << operator shifts the value 1 left the number of positions specified. (Zero positions in this case, which makes the operator unnecessary in this particular case.)You would then basically give names to each of the bit positions and use them something like this:Code:// Put these declarations outside of all functions toward the top of the code...const unsigned char XCmd = 0; // Bit zero. The LSB.const unsigned char YCmd = 1; // Bit one.const unsigned char ZCmd = 2; // Bit two.// Etc.// Some point later in the code...if (Commands & (1 << ZCmd)){ // The Z command is set!SelfDestruct();}

author: shebu18
author_rank: Frequent Contributor
timestamp: September 23, 2012, 09:26:44 am
content: bool BitSetting = Commands & (1 << BitPosition);In this row you use the AND to compare the byte you have stored in the command but with a byte where you have moved a 1 left by BitPosition places. if the and gives one, the bool also sets as one, so the command is "ON", if the AND is 0, the Bool is 0 and the command is "OFF".Right, i try to understand that and i wnat to be sure i understanded it right.Thank you once again!

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 23, 2012, 02:15:12 pm
content: You are correct. That's exactly what's happening.