Python与Arduino的串口读写

24
我正在尝试在Python代码和Arduino代码之间来回传递信息。我希望定期(例如每分钟)向Arduino代码发送两个设定值,然后在Arduino上读取这些值并更新变量,最后周期性地(例如每30秒)将状态信息从Arduino发送回Python。最终,Python将从MySQL数据库中发送和提取信息(稍后开发)。
目前,我无法可靠地使信息来回反弹。在搜索中我没有找到任何接近的东西,而且我尝试修改的所有内容都不起作用。我最接近的是以下内容(它实际上不能在发送和接收之间切换):
Python
#!/usr/bin/python
import serial
import syslog
import time

#The following line is for serial over GPIO
port = '/dev/ttyS0'


ard = serial.Serial(port,9600,timeout=5)

i = 0

while (i < 4):
    # Serial write section

    setTempCar1 = 63
    setTempCar2 = 37
    ard.flush()
    setTemp1 = str(setTempCar1)
    setTemp2 = str(setTempCar2)
    print ("Python value sent: ")
    print (setTemp1)
    ard.write(setTemp1)
    time.sleep(4)

    # Serial read section
    msg = ard.readline()
    print ("Message from arduino: ")
    print (msg)
    i = i + 1
else:
    print "Exiting"
exit()

Arduino:

// Serial test script

int setPoint = 55;
String readString;

void setup()
{

  Serial.begin(9600);  // initialize serial communications at 9600 bps

}

void loop()
{
  while(!Serial.available()) {}
  // serial read section
  while (Serial.available())
  {
    if (Serial.available() >0)
    {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    }
  }

  if (readString.length() >0)
  {
    Serial.print("Arduino received: ");  
    Serial.println(readString); //see what was received
  }

  delay(500);

  // serial write section

  char ard_sends = '1';
  Serial.print("Arduino sends: ");
  Serial.println(ard_sends);
  Serial.print("\n");
  Serial.flush();
}

我最终得到的只是重复的相同值(不是实际发送的内容,不确定是字符串还是字节问题),并且没有返回给Python脚本。非常感谢任何帮助或想法。谢谢。

编辑:按照下面的建议修改了代码。Arduino正常接收,并通过minicom验证了串行通信。但Python脚本仍然在“来自Arduino的消息:”后打印一个空行。


为什么在Python中读写串口时需要在两者之间关闭串口? - Peter Gibson
我曾试图使用该方法来强制交换读取和写入。我在论坛上读到过这样做而不是刷新的建议。 - Boombrewer
1
当我在Windows上尝试运行它时,我收到了“serial”没有属性“Serial”的错误。 你知道怎么回事吗? - Kostas
3个回答

15

在 Python 中写入和读取之间不应该关闭串口。存在这样一种情况,当 Arduino 响应时,端口仍然关闭,此时数据将会丢失。

while running:  
    # Serial write section
    setTempCar1 = 63
    setTempCar2 = 37
    setTemp1 = str(setTempCar1)
    setTemp2 = str(setTempCar2)
    print ("Python value sent: ")
    print (setTemp1)
    ard.write(setTemp1)
    time.sleep(6) # with the port open, the response will be buffered 
                  # so wait a bit longer for response here

    # Serial read section
    msg = ard.read(ard.inWaiting()) # read everything in the input buffer
    print ("Message from arduino: ")
    print (msg)

Python中的Serial.read函数默认只返回单个字节,因此您需要在循环中调用它或等待数据传输完成然后读取整个缓冲区。

在Arduino端,当没有数据可用时,您应该考虑在loop函数中发生什么。

void loop()
{
  // serial read section
  while (Serial.available()) // this will be skipped if no data present, leading to
                             // the code sitting in the delay function below
  {
    delay(30);  //delay to allow buffer to fill 
    if (Serial.available() >0)
    {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    }
  }

相反,在loop函数开始时等待数据到达:

void loop()
{
  while (!Serial.available()) {} // wait for data to arrive
  // serial read section
  while (Serial.available())
  {
    // continue as before

编辑2

当我从Python与你的Arduino应用程序进行接口时,我得到了以下结果:

>>> import serial
>>> s = serial.Serial('/dev/tty.usbmodem1411', 9600, timeout=5)
>>> s.write('2')
1
>>> s.readline()
'Arduino received: 2\r\n'

看起来那部分运作正常。

在测试你的Python脚本时,似乎问题是当你打开串口时Arduino会重置(至少我的Uno会),所以你需要等待几秒钟才能启动。你也仅读取了单行响应,因此我在下面的代码中已经修复了这个问题:

#!/usr/bin/python
import serial
import syslog
import time

#The following line is for serial over GPIO
port = '/dev/tty.usbmodem1411' # note I'm using Mac OS-X


ard = serial.Serial(port,9600,timeout=5)
time.sleep(2) # wait for Arduino

i = 0

while (i < 4):
    # Serial write section

    setTempCar1 = 63
    setTempCar2 = 37
    ard.flush()
    setTemp1 = str(setTempCar1)
    setTemp2 = str(setTempCar2)
    print ("Python value sent: ")
    print (setTemp1)
    ard.write(setTemp1)
    time.sleep(1) # I shortened this to match the new value in your Arduino code

    # Serial read section
    msg = ard.read(ard.inWaiting()) # read all characters in buffer
    print ("Message from arduino: ")
    print (msg)
    i = i + 1
else:
    print "Exiting"
exit()

现在的输出如下:

$ python ardser.py
Python value sent:
63
Message from arduino:
Arduino received: 63
Arduino sends: 1


Python value sent:
63
Message from arduino:
Arduino received: 63
Arduino sends: 1


Python value sent:
63
Message from arduino:
Arduino received: 63
Arduino sends: 1


Python value sent:
63
Message from arduino:
Arduino received: 63
Arduino sends: 1


Exiting

我按照您的建议修改了代码,现在它的表现更好了,但是我仍然无法从Arduino接收到任何东西。我已经将延迟时间延长到30秒,等待缓冲区,但仍然没有任何反应。 - Boombrewer
我在终端输入“来自arduino的消息:”后得到了一个空行,Arduino IDE上的串行监视器显示正在发送,每次读取时readstring都在增加。 - Boombrewer
你确认你能够在 Python 之外从 Arduino 接收数据吗?比如在 Hyperterm 或 miniterm.py 中? - Peter Gibson
1
我使用了minicom,并且串口通信肯定是双向的。虽然我不得不将arduino代码从byte ard_sends = 1;更改为char ard_sends ='1',但然后在minicom和arduino IDE中的串行监视器中它表现良好。然而,我的Python代码仍然存在相同的问题,并且只打印一条空白线作为“来自arduino的消息:”。 - Boombrewer
我甚至尝试了在这里找到的几个关于使用Python读取串行数据(例如使用if语句、数组等)的其他答案,但似乎找不到解决方案。顺便说一下,感谢您的帮助。 - Boombrewer
显示剩余4条评论

3

首先,您需要安装一个名为Serial的模块。要做到这一点,请转到Python安装文件夹中位于Scripts文件夹内,如果您使用的是Python 3版本,则通常位于以下位置:

C:\Python34\Scripts  

打开该文件夹后,按住Shift键右键单击该文件夹。然后点击“在此处打开命令窗口”。之后将会弹出cmd窗口。在cmd窗口中输入以下代码:

pip install PySerial

在此之后,按下回车键。然后PySerial模块就会被安装。请注意,在安装该模块之前,您必须拥有互联网连接。


安装成功后,打开Python IDLE并输入以下代码,然后运行它。

import serial
# "COM11" is the port that your Arduino board is connected.set it to port that your are using        
ser = serial.Serial("COM11", 9600)
while True:
    cc=str(ser.readline())
    print(cc[2:][:-5])   

1
我发现使用命令Serial.readString()代替Serial.read()可以更好地获取Arduino的连续输入/输出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接