Arduino串行数据解析
我正在编写一个应用程序,用我的Android手机通过蓝牙控制我的机器人,一切都很顺利,数据得到了回音和验证,但我在协议上遇到了一些问题,特别是当我发送一个命令,如Arduino串行数据解析,arduino,Arduino,我正在编写一个应用程序,用我的Android手机通过蓝牙控制我的机器人,一切都很顺利,数据得到了回音和验证,但我在协议上遇到了一些问题,特别是当我发送一个命令,如s,10100或s,-30,-10时,我希望我的机器人的轮子转动。。。(值以百分比表示) 我的问题是,当我想在Arduino上解析我的轮速命令时,我必须解析多达4个独立的字节到int,例如s,-100,-100使我的机器人全速后退,但我如何解析它,以便调用setSpeed(左,右)左侧和右侧等于-100 我知道我可以分别分析每个字节,然
s,10100
或s,-30,-10
时,我希望我的机器人的轮子转动。。。(值以百分比表示)
我的问题是,当我想在Arduino上解析我的轮速命令时,我必须解析多达4个独立的字节到int
,例如s,-100,-100
使我的机器人全速后退,但我如何解析它,以便调用setSpeed(左,右)代码>左侧<代码>和右侧<代码>等于-100
我知道我可以分别分析每个字节,然后把它们放在一起得到一个整数,但它不是很优雅,可能已经有更好的解决方案了,不幸的是,我还没有找到它
编辑
下面是用于解析命令的Arduino函数:
void parseCommand(char* command, int* returnValues)
{
// parsing state machine
byte i = 2, j = 0, sign = 0;
int temp = 0;
while(*(command + i) != '\0')
{
switch(*(command + i))
{
case ',':
returnValues[j++] = sign?-temp:temp;
sign = 0;
temp = 0;
break;
case '-':
sign = 1;
break;
default:
temp = temp * 10 + *(command + i) - 48;
}
i++;
}
// set last return value
returnValues[j] = sign?-temp:temp;
}
当解析类似于s,100,-100
(必须\0
终止)的内容时,您可以这样称呼它:
只需将一个字符一个字符地读入状态机。它简单而有效
要逐位读取数字,请执行以下操作:从零开始。对于每个数字,将数字乘以10,然后将数字的值相加。例如,阅读97的工作原理如下:
如果读入的数字没有前面的数字,则从0开始
你读入9并计算(0*10)+9->9
你读入7并计算(9*10)+7->97
如果读取非数字,则输出97
下面是一个更完整的示例s,10100
:
从“准备读取命令状态”开始
你读“s”,s是命令。切换到“准备读取第一个逗号”状态
读取第一个逗号,切换到“准备计算第一个参数的符号”状态
你读一个数字。因为这不是“-”,所以第一个参数是正的。将第一个数字设置为数字1的值。您现在处于“读取第一个数字”状态
你读一个数字,0。将第一个数字设置为1*10+0->10。您仍处于“读取第一个数字”状态
你读的是逗号。您现在处于“准备找出第二个参数的符号”状态
你读1。第二个数字是正数(因为它不是“-”号)。您将第二个数字设置为1。您处于“读取第二个数字”状态
你读了0。第二个数字现在设置为1x10+0->10。您仍处于“读取第二个数字”状态
你读了0。第二个数字现在设置为10x10+0->100。您仍处于“读取第二个数字”状态
你读了一行的结尾。执行结果:命令为“s”,第一个数字为正,第一个数字为10,第二个数字为正,第二个数字为100
切换回“准备读取命令”状态
我喜欢你的答案,但我想我会扮演魔鬼代言人的角色
以二进制形式读取数据可能很优雅,这取决于您需要对它做什么
在下面的示例中,从串行数据读取数据,直到它看到二进制分隔符0X7F
。读取的字节存储在inData
char数组中。请查看以下文档:
charindata[16];
面包;
bRead=Serial.readBytesUntil(0x7F,inData,4);
然后可以将该字节转换为整数或进行其他操作。请记住,最大值为+/-126,因为这是一个有符号字符(127是分隔符,不会被视为值)
您可以通过以下方式访问这些值:
Serial.print(“字节读取:”);
序列号:println(面包);
Serial.println(“第一字节”);
Serial.println((int)inData[0]);
Serial.println(
地图((int)inData[0],0126,01024)
);
Serial.println(“第二字节”);
Serial.println((int)inData[1]);
我用下面的bash命令测试了这一点(在确保串行速度设置正确后):
echo-ne'\x01\x02\x7F'>/dev/ttyACM0
我写的一些粗略的示例代码可以找到你刚刚打败了我,再次:(一个逐字符的状态机就像你能得到的一样灵活、可维护和可扩展。随着新命令的添加,涉及大量库函数的解决方案很快就会变得混乱。似乎有很多麻烦,我认为这是一个相对常见的问题,所以可能现有的函数可以做到这一点……状态机就是这样。当你在控制机器人的其他方面取得进展时,知道非法命令肯定会被检测到、拒绝,并发出一条精确的错误消息,指出出错的地方以及在命令行中发生的位置,这是一堆比从一大堆带字符串的库函数中的某个地方发出的segfault更好的错误到处都是索引,非常漂亮,非常优雅。你可以看看SparkFun的系列。源代码是可用的,它们做的事情非常相似。那些避免二进制操作的人是魔鬼的拥护者,我的朋友friend@Siidheesh有些人是出于固执而鼓吹,有些人是出于无知。不幸的是,在这种情况下,我属于后者
char serialData[16];
void loop()
{
if(Serial.available() > 0)
{
Serial.readBytesUntil('\0', serialData, 15);
switch(serialData[0])
{
case 's':
int speed[2];
parseCommand(serialData, speed);
setSpeed(speed[0], speed[1]);
break;
}
// always echo
Serial.write(serialData);
// end of message is maked with a \0
Serial.print('\0');
// clear serialData array
memset(serialData, 0, sizeof(serialData));
}
}