将数据从C发送到python

时间:2013-12-12 17:04:14

标签: python c

我在嵌入式处理器上运行了一些C代码,它使用printf("%d", my_int);在串行端口(stdout)上输出整数(16位)。我在PC上运行一些python代码,使用pyserial读取串行流并将其写入文件,我可以使用ser.readline()来实现。尽管效率不高,但这很愉快。

由于嵌入式C发送了相当多的数字并且我想减少传输时间,我想发送格式为二进制而不是ascii的数据(即两个字节/字符而不是几个字节/字符。)我试着这样做:

char msbs = my_int>>8;
char lsbs = my_int;
printf("%c%c\n", msbs, lsbs)

然后在我的python代码中:

chars = ser.readline()
value = ord(chars[0])<<8 + ord(chars[1])

但价值似乎是胡言乱语。任何人都可以指出我做错了吗?

3 个答案:

答案 0 :(得分:4)

试试这个:

import struct
chars = ser.readline()[:-1]
value = struct.unpack('>h', chars)

我假设你的整数是短的,请注意'&gt;'的存在for endianness(字节顺序)。

然而,您的代码错误是由于运算符优先级: value = (ord(chars[0])<<8) + ord(chars[1])这应该有效。但是最好还是使用struct。

答案 1 :(得分:0)

  

由于嵌入式C发送了大量数据,我想减少传输时间,

为了提高时间性能,您可以一次发送和/或接收多个号码。在我的测试中,array.fromfile()struct.unpack()快10到100倍。它的代价是有时会调用array.byteswap()来明确地考虑字节序。


如果C程序和Python脚本在同一台机器上运行(相同大小,相同的endianess);然后你可以使用fwrite在C端写入短整数作为平台值,并array.fromfile() on Python side以原始格式读回它们。

例如,打印短整数为二进制:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  short a[] = {31415, 9265, 3589, 793};
  size_t n = sizeof a / sizeof *a;
  if (fwrite(&n, sizeof n, 1, stdout) != 1) exit(EXIT_FAILURE); /* send size */
  return (fwrite(a, sizeof *a, n, stdout) < n) ? EXIT_FAILURE : EXIT_SUCCESS;
}

用Python阅读:

#!/usr/bin/env python3
import sys
import array
import struct

# make stdin binary
file = sys.stdin.detach()

# read size
size_format = 'N' # size_t
n, = struct.unpack(size_format, file.read(struct.calcsize(size_format)))
print(n)

a = array.array('h') # native short int
a.fromfile(file, n)
print(a.tolist()) # -> [31415, 9265, 3589, 793]

array.fromfile在时间和记忆方面都应该有效。如果您不知道尺寸,请致电a.fromfile,直到EOFError被提出。


如果C程序和Python脚本在不同的机器上,那么您可以按网络字节顺序发送整数:

#include <stdio.h>
#include <stdlib.h>

#include <netinet/in.h> /* htons() */

int main(void) {
  short a[] = {31415, 9265, 3589, 793};
  /* print one integer at a time */
  short *p = a, *end = &a[sizeof a / sizeof *a];
  for ( ; p != end; ++p) {
    uint16_t s = htons(*p); /* convert from native to network byte order */
    if (fwrite(&s, sizeof s, 1, stdout) != 1)  exit(EXIT_FAILURE);
  }
  return 0;
}

如果需要,在Python端交换字节顺序:

#!/usr/bin/env python
import array
import sys

a = array.array('h') # short int in native byte order, byte swap might be needed
for i in range(15, 128): 
    try: # double size to avoid O(n**2) behaviour
        a.fromfile(sys.stdin, 2 << i)
    except EOFError:
        break
if sys.byteorder != 'big': # if not network order
    a.byteswap()  # swap byte order
print(a.tolist()) # -> [31415, 9265, 3589, 793]

为避免转换为网络订单,您可以发送一个幻数。它允许在C端以本机字节顺序发送数字(如第一个代码示例中所示),并在Python中检查它以在必要时交换字节:

MAGIC = 1
if a[0] != MAGIC:
   a.byteswap()
   if a[0] != MAGIC:
      raise ValueError("Unexpected %d" % a[0])

答案 2 :(得分:0)

struct想法很棒,但你的协议有问题。

如果您编写二进制数据,则在其间使用行结尾是没有用的:其中一个&#34;值字节&#34;也可以有这个值 - 10 - 。

所以最好不要使用这些行结尾和readline()

但是可能会出现您可能不同步的问题。因此,您应该定义一种包边界,或者您可以用超过2个字节对​​数据进行编码。

示例:每个16位值以3个字节编码,采用以下方式:

AADDDDDD BBBDDDDD CCCDDDDD

AA10BBB110CCC111

值57723 - 0xE17B,0b1110000101111011,编码为

10111000 11001011 11111011

B8 CB FB

通过

byte1 = 0x80 + ((value >> 10) & 0x3F) # bits 10..15
byte2 = 0xC0 + ((value >> 5)  & 0x1F) # bits 5..9
byte3 = 0xE0 + ((value)       & 0x1F) # bits 0..4

收到这些字节后,您可以立即检测到

a)它是哪个字节(第1个,第2个或第3个)和 b)其内容。

因此,即使一个字节丢失,您也可以检测到该字节并立即恢复流的接收。

另外,这种实现与字节序无关 - 它可以在每台机器上运行,无论其字节顺序如何,都应该设计每种协议。

如何实施,我将留给您。

相关问题