EEVblog Electronics Community Forum
Electronics => Microcontrollers => Topic started by: shebu18 on September 20, 2012, 09:01:57 am
-
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:
if serial available>0
count =0
if serial.read="," then count++
array[count]=serial.read
Could this work?
-
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?
-
Have a look at C functions atoi and memmove of the string.h header.
http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/ (http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/)
http://www.cplusplus.com/reference/clibrary/cstring/memmove/ (http://www.cplusplus.com/reference/clibrary/cstring/memmove/)
Alexander.
-
RS232 it's a frame-oriented protocol, with either 7 or 8 bits per frame.
Basically, there are two approaches:
I) Serialisation
Assuming 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:
tmp = Serial.read();
tmp = tmp << 8;
tmp |= Serial.read();
II) ASCII
You 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
-
I wnat to format the string that i send from the PC to something like "U16483A4095X0Y1Z0"
U, voltage, =16483
A, current, =4095
X, command 1, =0
Y, command 2, =1
Z, command 3, =0
The 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.
-
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)...
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[0] (single and 2 dimension array respectively), you send it &data[1] or &data[0,1]
-
Something like this?
#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 value
temp *= 10;
temp += serialInput;
}
else
{
// Assign previously read value
switch (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 reading
switch (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.
-
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 = 0x5A
Define an array of five integer (4 bytes variabiles) ... Initialize all with 0
Initialize 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" digit
if 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 Z
so 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 program
if 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 here
array_position = byte - 0x56; // so position becomes a value between 0 and 4
else
array [ array_position] = array [array_position] * 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 serial
end 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.
-
I wnat to format the string that i send from the PC to something like "U16483A4095X0Y1Z0"
U, voltage, =16483
A, current, =4095
X, command 1, =0
Y, command 2, =1
Z, command 3, =0
The 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.
Parsing 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:
#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;
}
(https://www.eevblog.com/forum/microcontrollers/pc-arduino-serial-communication/?action=dlattach;attach=30459)
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.
-
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.
-
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:
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);
}
}
(https://www.eevblog.com/forum/microcontrollers/pc-arduino-serial-communication/?action=dlattach;attach=30476)
-
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?
-
There are a couple of ways to do that.
Here's one way:
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);
}
}
}
(https://www.eevblog.com/forum/microcontrollers/pc-arduino-serial-communication/?action=dlattach;attach=30478)
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:
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:
// 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();
}
-
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!
-
You are correct. That's exactly what's happening.