Arduino sscanf第一次正确解析CSV,但随后返回垃圾数据或使板崩溃

wkftcu5l  于 2023-01-15  发布在  其他
关注(0)|答案(2)|浏览(226)

this post之后,我找到了一种使用sscanf解析串行输入CSV格式数据的简洁方法:

volatile byte state = LOW;
volatile byte state_ = state;

long val1 = 100; 
long val1_ = val1; // buffer

long val2 = 500; 
long val2_ = val2; // buffer

String inpuSerialBufferString = "";

volatile byte control = LOW; 
volatile byte control_ = control;

void reportSerial() {
  Serial.println(String(millis()) + ", " + String(control) + ", " + 
        String(state) + ", " + String(val1) + ", " + String(val2));
}

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

void loop() {
  if(Serial.available() > 0)
  {
    inpuSerialBufferString = Serial.readStringUntil('\n');
    sscanf(inpuSerialBufferString.c_str(), "%d,%d,%d,%d", &control, &state, &val1, &val2);
  }

  if (val1 != val1_ || val2 != val2_ || state != state_ || control != control_) {
    reportSerial();

    val1_ = val1;
    val2_ = val2;
    state_ = state;
    control_ = control;
  }
}

例如,在Arduino IDE的串行监视器发送中,我放置了以下值:
1,0,300,200
0,1,400,100
0,1,600,200
我得到
十一六二八、一、零、三百、二百
22260、0、1、741343632、741408868
41760、0、1、741343832、741408968
或者有时它不返回任何东西。如果您能帮助我了解问题所在以及如何修复它,我将不胜感激。代码中的优先级应该是
1.安全性:系统应具有可预测的行为
1.业绩受到赞赏
1.代码最好是简洁易读

    • P.S.**我没有在Arduino StackExchange发帖,因为我在那里找不到任何标签csvscanfparsing
r8xiu3jd

r8xiu3jd1#

我想我已经解决了这个问题。从this post使用strtok标记化C字符串和atoi进行C字符串到整数的转换,到目前为止我得到了可靠的结果。

sscanf(inpuSerialBufferString.c_str(), "%d,%d,%d,%d", &control, &state, &val1, &val2);

char* pch;
    char* inpuSerialBufferCstring = inpuSerialBufferString.c_str();
    pch = strtok(inpuSerialBufferCstring, ",");
    control = atoi(pch);
    pch = strtok(NULL, ",");
    state = atoi(pch);
    pch = strtok(NULL, ",");
    val1 = atoi(pch);
    pch = strtok(NULL, ",");
    val2 = atoi(pch);

有一个小问题,我得到以下警告:
在函数“void loop()”中:
警告:从“const char*”到“char*”的转换无效[-fpermissive]
char* 输入序列缓冲区字符串=输入序列缓冲区字符串.c_str();

附言感谢@datafiddler替换

char* inpuSerialBufferCstring = inpuSerialBufferString.c_str();
pch = strtok(inpuSerialBufferCstring, ",");

char inputBuffer[50] ; 
Serial.readBytesUntil('\n', inputBuffer, 50);
pch = strtok(inputBuffer, ",");

**P.S.2.**CSV中的最后一位数字应该是4位数,不能少,不能多。所以如果你想放50,你需要发送0050。我不知道为什么!

bn31dyow

bn31dyow2#

sscanf依赖于格式字符串来指定目标变量的大小。
特别是,状态和控制是字节。您需要使用“hh”修饰符(C99)来读取字符长度变量,并使用%hhd。或者,您需要使用临时int变量来阅读%d,然后将赋值转换为字节变量。
您还为val 1和val 2使用了%d,但它们是long int,因此格式字符串需要“l”修饰符-- %ld。
当sscanf将int值写入char变量时,它将写入int范围的值(16位或32位,具体取决于平台),结果不可预测。
如果变量在堆栈中,则会损坏堆栈,这很可能导致崩溃。

相关问题