【问题标题】:Arduino inputString.indexOf with multiple variables具有多个变量的 Arduino inputString.indexOf
【发布时间】:2021-05-09 12:48:10
【问题描述】:

我目前正在制作一个 arduino 监控设备。数据在 Python 中收集,然后通过串口将字符串发送到 arduino。

在 Python 中,字符串如下所示:

    cpu1 = space_pad(int(my_info['cpu_load']), 2)
    cpu2 = space_pad(int(my_info['cpu_temp']), 2)
    cpu3 = space_pad(int(my_info['cpu_fan']), 5)

    # Send the strings via serial to the Arduino
    arduino_str = \
        'A' + cpu1 + '|B' + cpu2 + '|C' + cpu3 + '|'
    if serial_debug:
        print(arduino_str)
    else:
        ser.write(arduino_str.encode())

理想情况下,我想让这个字符串尽可能大,包括 10 个变量,我想将它们发送到 arduino。

arduino 代码查看字符串,它应该读取字符串的一部分并将它们整齐地放置在显示器上,每个都在它自己的保留空间中。

问题是我得到了乱码的结果。当字符串仅由一个变量组成时,它会显示得很好,应该在哪里,应该在哪里。

当向字符串添加一个额外的变量时,代码会中断,它会混合结果或混乱地显示它们。我的变量都是干净的,只是数字,没什么花哨的。

下面是我在arduino上使用的代码

#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD

LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows

String inputString = "";            // String for buffering the message
boolean stringComplete = false;     // Indicates if the string is complete
unsigned long previousUpdate = 0;   // Long to keep the time since last received message

void printInitialLCDStuff() {
  lcd.setCursor(0, 0);
  lcd.print("CPU ");
  lcd.setCursor(7, 0);
  lcd.print("%");
  lcd.setCursor(11, 0);
  lcd.print("C");
  lcd.setCursor(17, 0);
  lcd.print("RPM");
  lcd.setCursor(0, 1);
  lcd.print("GPU ");
  lcd.setCursor(7, 1);
  lcd.print("%");
  lcd.setCursor(11, 1);
  lcd.print("C");
  lcd.setCursor(17, 1);
  lcd.print("RPM");
  lcd.setCursor(0, 2);
  lcd.print("MEM");
  lcd.setCursor(8, 2);
  lcd.print("MB"); 
  lcd.setCursor(17, 2);
  lcd.print("PWM");
  lcd.setCursor(0, 3);
  lcd.print("RAM ");
  lcd.setCursor(8, 3);
  lcd.print("GBU");
  lcd.setCursor(17, 3);
  lcd.print("GBF");
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    if (inChar == '|') {
      stringComplete = true;
    }
  }
}

void setup() {
  // Setup LCD
  lcd.init(); //initialize the lcd
  lcd.backlight(); //open the backlight
  lcd.setCursor(0, 0);
  printInitialLCDStuff();

  // Setup serial
  Serial.begin(9600);
  inputString.reserve(200);
}

void loop() {
  serialEvent();
  if (stringComplete) {

    // CPU1
    int cpu1StringStart = inputString.indexOf("A");
    int cpu1StringLimit = inputString.indexOf("|");
    String cpu1String = inputString.substring(cpu1StringStart + 1, cpu1StringLimit);
    lcd.setCursor(4, 0);
    lcd.print(cpu1String);

    // CPU2
    int cpu2StringStart = inputString.indexOf("B", cpu1StringLimit);
    int cpu2StringLimit = inputString.indexOf("|", cpu2StringStart);
    String cpu2String = inputString.substring(cpu2StringStart + 1, cpu2StringLimit);
    lcd.setCursor(9, 0);
    lcd.print(cpu2String);

    // CPU3
    int cpu3StringStart = inputString.indexOf("C", cpu2StringLimit);
    int cpu3StringLimit = inputString.indexOf("|", cpu3StringStart);
    String cpu3String = inputString.substring(cpu3StringStart + 1, cpu3StringLimit);
    lcd.setCursor(13, 0);
    lcd.print(cpu3String);    

    inputString = "";
    stringComplete = false;
    previousUpdate = millis();

    }
  }

我的代码很脏,而且主要是对另一个代码的改编,因为虽然我可以阅读代码,但我不擅长编写它。如果我犯了让任何人畏缩的可怕错误,我深表歉意。我承认我只是涉足编码。这也是我经常在代码中做注释的原因。

我希望我的显示器显示如下:

CPU  60% 45C  900RPM

在哪里

cpu1=60
cpu2=45
cpu3=900

“CPU”、“%”、“C”和“RPM”是由 arduino 在 printInitialLCDStuff() { 而不是 Python 上编写的。

相反,我得到了这个

CPU B45% B45|B45|C

然后 RPM 在第 3 行 (0,0) 列为"900|"

理想情况下,我想扩展字符串排序以收集大约 10 个变量。

在我看来,问题出在 arduino 代码中,因为 Python 脚本有点检查并正确输出字符串。但我可能是错的。

问题是:我是否使用了错误的代码来提取这些变量并将它们放在显示器上的保留空间中?

我应该使用其他东西来完成工作吗?过去 3 天我一直在查看文档,但找不到有类似情况的人。我在这里发现了一些问题,但又不是我想要的。

感谢任何帮助。在过去几天每天尝试数小时后,我对这段代码感到非常沮丧,我愿意用 Steam 数字礼品卡奖励任何可以帮助我使用此代码的人,以表达我的感激之情。

最好的问候, M

【问题讨论】:

  • 首先,您的python代码中的前缀A、B是多余的,将数据发送为cpu1|cpu2|cpu3会更容易更好。这将使解析更容易。
  • 其次,Arduino是C++,尽量学习C++,不要用python的方式写Arduino代码。了解如何使用strtok 将数据解析为数组,然后您可以循环遍历数组并将其打印到显示器上。
  • 嗨@hcheung,感谢您抽出宝贵时间回答这个问题。我有点理解你对 A、B 是多余的意思的理解,但我不太确定如何在它们缺席的情况下以一种可行的方式定义代码?什么会限制每个较小的字符串“提取”?至于学C++,我有一点时间就去学,这很难得。

标签: python arduino indexof


【解决方案1】:

我想通了。我没有尝试分离字符串并将其全部分配在预选的空间中,而是创建了一个字符串并编辑了字符串格式本身。现在我有一个 80 字符的字符串,由 (0, 20), (20, 40), (40, 60) 和 (60, 80) 自动排列。

因为这只是一个简单的资源监视器,我真的不需要任何花哨的东西,只需在屏幕上显示信息。

这就是我所做的

# Prepare CPU string line #1
        cpu1 = space_pad(int(my_info['cpu_load']), 3) + '% '
        cpu2 = space_pad(int(my_info['cpu_temp']), 2) + 'C '
        cpu3 = space_pad(int(my_info['cpu_fan']), 4) + 'RPM'

        CPU = 'CPU ' + cpu1 + cpu2 + cpu3
    
        # Prepare GPU string line #2
        gpu1 = space_pad(int(my_info['gpu_load']), 3) + '% '
        gpu2 = space_pad(int(my_info['gpu_temp']), 2) + 'C '
        gpu3 = space_pad(int(my_info['gpu_fan']), 4) + 'RPM'

        GPU1 = 'GPU ' + gpu1 + gpu2 + gpu3

        # Prepare GPU string line #3
        gpu4 = space_pad(int(my_info['gpu_mem']), 4) + 'MB  '
        gpu5 = space_pad(int(my_info['gpu_pwm']), 3) + '% PWM'

        GPU2 = 'MEM ' + gpu4 + gpu5

        # Prepare RAM strng line #4
        ram1 = space_pad(float(my_info['ram_used']), 4) + 'GBU  '
        ram2 = space_pad(float(my_info['ram_free']), 4) + 'GB'

        RAM = 'RAM ' + ram1 + ram2

        # Send the strings via serial to the Arduino
        arduino_str = \
            CPU + GPU1 + GPU2 + RAM + 'F'

        if serial_debug:
            print(arduino_str)
        else:
            ser.write(arduino_str.encode())

因为'|'分隔符不断显示为字符串的最后一个字符,我只是切换到一个我想出现的字母,基本上是鸭式录音。我使用了字符“F”,它也充当字母并显示在字符串中。

至于 Arduino 代码:

#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD

LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows

String inputString = "";            // String for buffering the message
boolean stringComplete = false;     // Indicates if the string is complete
unsigned long previousUpdate = 0;   // Long to keep the time since last received message

void printInitialLCDStuff() {
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    if (inChar == 'F') {
      stringComplete = true;
    }
  }
}

void setup() {
  // Setup LCD
  lcd.init(); //initialize the lcd
  lcd.backlight(); //open the backlight
  printInitialLCDStuff();
  lcd.setCursor(0, 0);
  lcd.print("Arduino PC Monitor");
  lcd.setCursor(0, 1);
  lcd.print("Waiting for data...");
  lcd.setCursor(12, 3);
  lcd.print("Ver 1.0");

  // Setup serial
  Serial.begin(9600);
  inputString.reserve(200);
}

void loop() {
  serialEvent();
  if (stringComplete) {

    // 1st line
    String cpuString = inputString.substring(0, 20);
    lcd.setCursor(0, 0);
    lcd.print(cpuString);
    // 2nd line
    String gpu1String = inputString.substring(20, 40);
    lcd.setCursor(0, 1);
    lcd.print(gpu1String);
    // 3rd line
    String gpu2String = inputString.substring(40, 60);
    lcd.setCursor(0, 2);
    lcd.print(gpu2String);
    // 4th line
    String ramString = inputString.substring(60, 80);
    lcd.setCursor(0, 3);
    lcd.print(ramString);

    inputString = "";
    stringComplete = false;
    previousUpdate = millis();

    }
  }

是的,就是这么懒惰,几乎就像数手指头一样复杂。我喜欢它的简单。任何东西都可以即时编辑,所需要的只是最少的知识。

如果我因为这么懒而被封号,我可以理解。

感谢您的帮助。如果我需要做一些更复杂的事情,我已经注意到关于字符串拆分的建议。

最好的问候, M

【讨论】:

    【解决方案2】:

    可以更改您的代码以使其正常工作,但是,这就像修补它一样。我认为最好采取不同的方法。

    首先,在您的 Python 代码中,前缀 A、B 变得多余,对接收方解析数据没有帮助。如果您将数据格式化为字符串,并以| 作为分隔符,则更易于解析,并且在 Python 中创建类似的字符串也更容易。

        arduino_str = "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}".format(
             data0, data1, data2, data3, data4,
             data5, data6, data7, data8, data9)
             
        if serial_debug:
            print(arduino_str)
        else:
            ser.write(arduino_str.encode())
    

    在 Arduino 方面,Serial.readStringUntil() 会读取整个字符串,直到遇到 \n(字符串结尾)。收到整个字符串后,您可以使用 C++ 中的 strtok() 函数将字符串按分隔符(在本例中为 |)拆分为一个数组,因此拆分后的数组如下所示:

        splitted[0] = data0;
        splitted[1] = data1;
        ....
        splitted[9] = data9;
    

    然后您可以将数组中的数据打印到 LCD。

        #include <Wire.h>
        #include <LiquidCrystal_I2C.h> // Library for LCD
        
        #define NUMBER_OF_DATA 10
        
        LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows
        
        String incomingString = "";
        
        void setup() 
        {
          // Setup LCD
          lcd.init(); 
          lcd.backlight(); 
          lcd.setCursor(0, 0);
          lcd.setCursor(0, 0);
          lcd.print("CPU    %    C     RPM");
          lcd.setCursor(0, 1);
          lcd.print("GPU    %    C     RPM");
          lcd.setCursor(0, 2);
          lcd.print("MEM     MB        PWM");
          lcd.setCursor(0, 3);
          lcd.print("RAM     GBU       GBF");
        
          // Setup serial
          Serial.begin(9600);
        }
        
        void loop() 
        {
          // read data from Serial until '\n' is received
          while (Serial.available()) {
            incomingString = Serial.readBytesUntil('\n');
          }
          
          if (incomingString) {
            // convert the String object to a c_string
            char *c_string = incomingString.c_str();
            // make a copy of received data so that incoming data would not override the received data
            char temp[strlen(c_string)+1];
            strcpy(temp, c_string);
            incomingString = "";
        
            // parse the received string separated by '|' into an array
            char splitted[NUMBER_OF_DATA][10] = {'\0'};  // initialise an array of 10-ch string
            int i = 0;
            char *p = strtok(temp, '|');    // parse first element in the string
            while(p != NULL) {    // loop through the string to fill the array
              splitted[i++] = p;
              p = strtok(NULL, '|');
            }
        
            // update LCD with the data in the array
            lcd.setCursor(4, 0);
            lcd.print(splitted[0]);
            lcd.setCursor(9, 0);
            lcd.print(splitted[1]);
            lcd.setCursor(13, 0);
            lcd.print(splitted[2]);
            
            // print the rest of data
            
            lcd.setCursor(13, 3);
            lcd.print(splitted[9]); 
          }
        }
    

    我是根据您的代码编写的,还没有在 Arduino 上进行调试,所以如果它不能开箱即用,它可能需要一些调试。我希望这可以帮助你学习一些技巧和一点点 C++ 字符串和字符数组。

    【讨论】:

    • 嗨,我试图在 arduino 代码中编译它,并且在 if (n) { 我得到 'n' 没有在这个范围内声明。对我来说,它看起来应该可行,但我不确定,因为我在这里处于全新的领域。
    • 抱歉,n 应该是 incomingString,我做了更改,请参阅更新后的代码。正如我所说,您可能需要在此过程中进行一些调试......
    猜你喜欢
    • 2016-01-27
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2014-01-07
    相关资源
    最近更新 更多