简体   繁体   English

通过串行向 arduino 发送 8 位整数

[英]Send 8 bit integers over serial to arduino

I created a music visualizer and I want to send 3 8 bit integers (0-255) over serial to an Arduino using python's pyserial library I have a text file called rgb.txt on my computer which has the data: 8,255,255 .我创建了一个音乐可视化器,我想使用 python 的pyserial库通过串行发送3 个8 位整数(0-255)到 Arduino 我的计算机上有一个名为rgb.txt的文本文件,其中包含数据: 8,255,255 I am sending the data over serial with this code:我正在使用以下代码通过串行方式发送数据:

import serial, time
arduino = serial.Serial('COM3', 9600, timeout=.1)
time.sleep(2) #give the connection a second to settle

while True:
    with open("rgb.txt") as f:
        rgb = f.read().strip("\n")
    arduino.write(rgb.encode("ASCII"))

    data = arduino.readline()
    if data:
        try:
            print(data.decode('ASCII').strip("\r").strip("\n")) # (better to do .read() in the long run for this reason
        except UnicodeDecodeError:
            pass
    time.sleep(0.1)

And I'm receiving it with this code:我收到了这个代码:

#include <stdio.h>
int r = A0;
int g = A1;
int b = A2;
void setup() {
  Serial.begin(9600);
  analogWrite(r, 255); delay(333); analogWrite(r, 0);
  analogWrite(g, 255); delay(333); analogWrite(g, 0);
  analogWrite(b, 255); delay(334); analogWrite(b, 0);
}
void loop() {
  if(Serial.available() > 0) {
    char data = Serial.read();
    char str[2];
    str[0] = data;
    str[1] = '\0';
      Serial.println(str);
    }
  }

The output I am getting is我得到的 output 是

8
,
2
5
5
,
2
5
5

How can I parse it so I receive:我怎样才能解析它所以我收到:

8
255
255

And preferably in 3 different variables ( r g b ) Thank you!最好在 3 个不同的变量中( r g b )谢谢!

What you do now is read a char , turn it into a CString str , and then you println() it before you go on to the next char .你现在要做的是读取一个char ,把它变成一个 CString str ,然后在你 go 到下一个char之前println()它。

You could probably stick the bytes together the way you want from what you got, but it is easier to read the received bytes into a buffer and split the result:您可能可以按照您想要的方式将字节粘在一起,但是将接收到的字节读入缓冲区并拆分结果更容易:

Send the RGB values from Python separated with commas and with a '\n' on the end, and then on the Arduino do something like this (untested, but you get the idea):从 Python 发送 RGB 值,用逗号分隔,最后用'\n'分隔,然后在 Arduino 上做这样的事情(未经测试,但你明白了):

void loop() {
  static char buffer[12];
  static uint8_t rgb[3];

  if (Serial.available() > 0) {
    Serial.readBytesUntil('\n', buffer, 12);
    int i = 0;
    char *p = strtok(buffer, ",");
    while (p) {
      rgb[i++] = (uint8_t)atoi(p);
      p = strtok(NULL, ",");
    }
    // You now have uint8s in rgb[]
    Serial.println(rgb[0]);
    Serial.println(rgb[1]);
    Serial.println(rgb[2]); 
  }
}

Note: no checks and error handling in this code.注意:此代码中没有检查和错误处理。

There are no doubt prettier ways, but this came to mind first and I think it will work.毫无疑问,有更漂亮的方法,但首先想到的是,我认为它会起作用。 It could also be done using a String object, but I try not to.也可以使用字符串 object 来完成,但我尽量不这样做。

For the code in this other answer to work, some things need to be added (but I haven't tested if these additions are enough):为了使其他答案中的代码正常工作,需要添加一些东西(但我还没有测试这些添加是否足够):

void loop() {
  if (Serial.available() > 0) {
    char data = Serial.read();
    if (data < '0' || data > '9')
      rgbIndex++;
    else
      rgb[rgbIndex] = rgb[rgbIndex] * 10 + (data - 48);
    if (rgbIndex == 3) {
      // You now have uint_8s in rgb[]
      Serial.println(rgb[0]);
      Serial.println(rgb[1]);
      Serial.println(rgb[2]);
      rgbIndex = 0;
      for (int i=0; i<3; i++)
        rgb[i] = 0;
    }
  }
}

Note that converting what you read from the file to chars or integers on the Python side and simply sending three bytes would greatly simplify things on the Arduino side.请注意,将从文件中读取的内容转换为 Python 端的字符或整数并简单地发送三个字节将大大简化 Arduino 端的事情。

If you have always a ',' character, you can convert it to int.如果你总是有一个 ',' 字符,你可以将它转换为 int。

uint8_t rgb[3] = {0,0,0};
uint8_t rgbIndex = 0;

void loop() {
  if(Serial.available() > 0) {
    char data = Serial.read();
    if(data < '0' || data > '9')
      rgbIndex++;
    else
      rgb[rgbIndex] = rgb[rgbIndex] * 10 + (data - '0');
  }
}

Arduino Code: Arduino 代码:

#include <stdio.h>
int r = A0;
int g = A1;
int b = A2;
void setup() {
  Serial.begin(115200);
  analogWrite(r, 255); delay(333); analogWrite(r, 0);
  analogWrite(g, 255); delay(333); analogWrite(g, 0);
  analogWrite(b, 255); delay(334); analogWrite(b, 0);
}
void loop() {
  static char buffer[12];
  static uint8_t rgb[3];

  if (Serial.available() > 0) {
    Serial.readBytesUntil('\n', buffer, 12);
    int i = 0;
    char *p = strtok(buffer, ",");
    while (p) {
      rgb[i++] = (uint8_t)atoi(p);
      p = strtok(NULL, ",");
    }
    // You now have uint8s in rgb[]
    analogWrite(A0, rgb[0]);
    analogWrite(A1, rgb[1]);
    analogWrite(A2, rgb[2]); 
    Serial.println(rgb[0]);
    Serial.println(rgb[1]);
    Serial.println(rgb[2]);
  }
}

Python Code: Python 代码:

import serial, time
def run():
    arduino = serial.Serial('COM3', 115200, timeout=.1)
    time.sleep(2) #give the connection a second to settle

    while True:
        with open("rgb.txt") as f:
            rgb = f.read()
            rgb += "\n"
        arduino.write(rgb.encode("ASCII"))

        data = arduino.readline()
        if data:
            try:
                print(data.decode('ASCII').strip("\r").strip("\n")) # (better to do .read() in the long run for this reason
            except UnicodeDecodeError:
                pass
        time.sleep(0.1)
while True:
    try:
        run()
    except serial.serialutil.SerialTimeoutException:
        print("Is your com port correct?")

Your original code is almost correct.您的原始代码几乎是正确的。

You just need to change the format a little bit to synchronise to newline characters.您只需稍微更改格式即可与换行符同步。

Python Code Python代码

import serial, time
arduino = serial.Serial('COM3', 9600, timeout=.1)
time.sleep(2) #give the connection a second to settle

while True:
    with open("rgb.txt") as f:
        rgb = f.read()
        rgb = rgb + '\n'  # Add a newline character after the RGB values to aid sychronisation in the Arduino code.
    arduino.write(rgb.encode("ASCII"))

    data = arduino.readline()
    if data:
        try:
             print(data.decode('ASCII').strip("\r").strip("\n"))
        except UnicodeDecodeError:
            pass
    time.sleep(0.1)

Arduino Code Arduino 代码

I've used sscanf() to read the 3 integers from the buffer because it returns a count of the number of items it successfully scanned into variables.我使用sscanf()从缓冲区读取 3 个整数,因为它返回成功扫描到变量中的项目数的计数。 I've also added some #defines to make it more readable and maintainable.我还添加了一些#defines以使其更具可读性和可维护性。

#include <stdio.h>

#define PORT_R A0
#define PORT_G A1
#define PORT_B A2

void setup()
{
  Serial.begin(9600);
  analogWrite(PORT_R, 255); delay(333); analogWrite(PORT_R, 0);
  analogWrite(PORT_G, 255); delay(333); analogWrite(PORT_G, 0);
  analogWrite(PORT_B, 255); delay(334); analogWrite(PORT_B, 0);
}

void loop()
{
  uint8_t r, g, b;
  if (ReadRGB(r, g, b))
  {
    analogWrite(PORT_R, r);
    analogWrite(PORT_G, g);
    analogWrite(PORT_B, b);
    Serial.println(r);
    Serial.println(g);
    Serial.println(b);
  }
}

bool ReadRGB(uint8_t &r, uint8_t &g, uint8_t &b)
{
  if (Serial.available() > 0)
  {
    const int LENGTH = 13;  // nnn,nnn,nnn\r\0
    char buffer[LENGTH];
    size_t count = Serial.readBytesUntil('\n', buffer, LENGTH - 1);  // Allow room for NULL terminator.
    buffer[count] = 0;  // Place the NULL terminator after the last character that was read.
    int i = sscanf(buffer, "%d,%d,%d", &r, &g, &b);
    return i == 3;  // Notify whether we successfully read 3 integers.
  }
  return false;
}

Regarding Serial.parseInt() , it doesn't notify when it times out.关于Serial.parseInt() ,它不会在超时时通知。 Instead, it simply returns 0, which is a valid value, so the caller has no idea whether this was due to a timeout.相反,它只返回 0,这是一个有效值,因此调用者不知道这是否是由于超时造成的。

The same issue exists with Serial.readBytesUntil() because it doesn't notify the caller whether the returned byte count is the result of encountering the search character or enduring a timeout. Serial.readBytesUntil()也存在同样的问题,因为它不会通知调用者返回的字节数是遇到搜索字符还是超时。 What if 255,255,25 was received due to a timeout caused by a communication error instead of the expected 255,255,255 ?如果由于通信错误导致超时而不是预期的255,255,25而收到255,255,255怎么办? The caller would be unaware.来电者是不知道的。

Compare with the robust methodology of int.TryParse() in C#.NET which returns a bool to indicate success/failure and passes the parsed int by reference.与 C#.NET 中强大的int.TryParse()方法相比,它返回一个bool来指示成功/失败,并通过引用传递解析的int

More Robust Arduino Code更强大的 Arduino 代码

To overcome the issues of Serial.parseInt() and Serial.readBytesUntil() timing out without returning an error code when the serial input buffer is empty, it's possible to use a non-blocking algorithm algorithm , something like this which reads one character per loop() until it reaches a newline character before scanning the buffer for 3 integers:为了克服串行输入缓冲区为空时Serial.parseInt()和 Serial.readBytesUntil( Serial.readBytesUntil()超时而不返回错误代码的问题,可以使用非阻塞算法算法,类似于这样,每个读取一个字符loop()直到它到达一个换行符,然后扫描缓冲区以获取 3 个整数:

#define PORT_R A0
#define PORT_G A1
#define PORT_B A2

const int LENGTH = 12;  // nnn,nnn,nnn\0
char buffer[LENGTH];

void setup()
{
  Serial.begin(9600);
  Serial.println("setup()");
  analogWrite(PORT_R, 255); delay(333); analogWrite(PORT_R, 0);
  analogWrite(PORT_G, 255); delay(333); analogWrite(PORT_G, 0);
  analogWrite(PORT_B, 255); delay(334); analogWrite(PORT_B, 0);
}

void loop()
{
  Serial.println("loop()");
  uint8_t r, g, b;
  if (ReadRGB(r, g, b))
  {
    analogWrite(PORT_R, r);
    analogWrite(PORT_G, g);
    analogWrite(PORT_B, b);
    Serial.println(r);
    Serial.println(g);
    Serial.println(b);
  }
}

bool ReadRGB(uint8_t &r, uint8_t &g, uint8_t &b)
{
  if (ReadLine(buffer, LENGTH))
  {
    Serial.println("ReadRGB() read a line.");
    int i = sscanf(buffer, "%d,%d,%d", &r, &g, &b);
    return i == 3;  // Notify whether 3 integers were successfully read.
  }
  return false;
}

int ReadLine(char *buffer, const int length)
{
  static int index = 0;
  int last_index = 0;
  int ch = Serial.read();
  if (ch > 0)
  {
    switch(ch)
    {
      case '\r':
        break;
      case '\n':
        last_index = index;
        index = 0;
        return last_index;
      default:
        if (index < length - 1)
        {
          buffer[index++] = ch;
          buffer[index] = 0;
        }
    }
  }
  return 0;
}

If you want to send 3 bytes over serial, which is typically very low bandwidth, you really should just send 3 bytes rather than the ASCII representations, along with commas and newlines.如果你想通过串行发送 3 个字节,这通常是非常低的带宽,你真的应该只发送 3 个字节而不是 ASCII 表示,以及逗号和换行符。 If you send:如果您发送:

255,255,255

with a newline at the end, you are actually sending 12 bytes instead of the 3 you need.最后有一个换行符,你实际上是在发送 12 个字节而不是你需要的 3 个字节。

Say you have 3 variables, a , b and c you want to send:假设您要发送 3 个变量abc

a, b, c = 1, 8, 255

you can pack them as 3 unsigned bytes ( B ) like this:您可以将它们打包为 3 个无符号字节( B ),如下所示:

import struct

packet = struct.pack('3B',a,b,c)

Then if you check the contents, you will see your 3 bytes:然后,如果您检查内容,您将看到 3 个字节:

b'\x01\x08\xff'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM