将串行输出打印到Python程序时,ArduinoJson序列化空JSON数据

时间:2018-09-11 18:06:05

标签: python json arduino pyserial

我正在尝试在Arduino UNO板和Python程序之间建立两种通信方式。为交换的消息选择的格式为JSON。

由于Arduino UNO具有64字节的输入缓冲区,并且JSON消息远大于此,因此我实现了一种方法,可以使用Python将JSON数据分解为64字节消息,然后在Arduino代码上重新组装。

要标记一条消息的结尾,我使用字符串“ ”,通知Arduino和Python这条消息已完全传递。

通过Arduino串行监视器输入数据时,Arduino代码工作正常。我用作输入的JSON如下。两行,通过串行监视器发送,一次发送一次。

{"sequence": 0, "state": 0, "commands": [{"device_id": "1", "val
ue": 1.0, "result": 0}], "statuses": [{"device_id": "1"}]}<e>

串行监视器的输出是这样的:

enter image description here

但是当我用Python代码运行相同的Arduino代码时,生成并发送回Python的JSON为空('{}')。

这是我的Arduino代码:

#include <ArduinoJson.h>

String inputMessage = "";
bool processingRequest = false;

void serialEventRun(void) {
  if (Serial.available()) serialEvent();
}

void serialEvent() {

  if (!processingRequest) {

    String message = "";
    char c;

    while (Serial.available() > 0) {
      c = Serial.read();
      message.concat(c);
    }

    message.trim();

    inputMessage += message;

    if (inputMessage.endsWith("<e>")) {

      processingRequest = true;
      inputMessage = inputMessage.substring(0, inputMessage.indexOf("<e>"));

      const size_t bufferSize = 2 * JSON_ARRAY_SIZE(3) + JSON_OBJECT_SIZE(2) + 6 * JSON_OBJECT_SIZE(3) + 240;
      DynamicJsonBuffer jsonBuffer(bufferSize);

      JsonObject& root = jsonBuffer.parseObject(inputMessage);

      const int sequence = root["sequence"];
      const int state = root["state"];
      JsonArray& commands = root["commands"];
      JsonArray& statuses = root["statuses"];

      // TODO include real command/status call
      if (commands.size() > 0) {
        for (int i = 0; i < commands.size(); i++) {
          JsonObject& command = commands[i];
          command["result"] = 1;
        }
      }
      if (statuses.size() > 0) {
        for (int i = 0; i < statuses.size(); i++) {
          JsonObject& status = statuses[i];
          status["value"] = 1.1;
          status["health"] = 0;
        }
      }

      root["state"] = 2;

      root.printTo(Serial);
      Serial.print("<e>");

      processingRequest = false;

    }

  }

}

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

void loop() {}

这是我的Python代码:

import time

from serial import Serial


def main():

    result = ''

    serial_conn = Serial(port='COM5', baudrate=115200, timeout=0.1)
    time.sleep(1)
    serial_conn.write(str.encode('{"sequence": 0, "state": 0, "commands": [{"device_id": "1", "val'))
    time.sleep(0.1)
    serial_conn.write(str.encode('ue": 1.0, "result": 0}], "statuses": [{"device_id": "1"}]}<e>'))
    time.sleep(0.1)

    # serial_conn.flushInput()

    while True:
        # bytes_to_read = serial_conn.inWaiting()
        # msg = serial_conn.read(bytes_to_read)
        msg = serial_conn.readline()
        time.sleep(0.1)
        result += msg.decode()
        print("<{}>".format(result))
        if result.endswith('<e>'):
            break
    result = result.strip('<e>')
    print("Message received: <{}>".format(result))

    serial_conn.close()


if __name__ == '__main__':
    main()

使用Python运行时,这是控制台输出:

<{}<e>>
Message received: <{}>

Process finished with exit code 0

试图弄清楚发生了什么,我将Arduino代码更改为仅将JSON序列化数据的长度打印到串行端口。在串行监视器上运行时,长度为135,与上面的屏幕快照相同。但是,使用Python运行时,其长度为5,与我们在Python的控制台输出中看到的“ {} ”的长度完全相同。因此,显然,当我使用Python运行时,JSON序列化会生成空数据(“ {}”)。

我已经想过,如果我不应该打印到串行端口(在Arduino代码中),也要像接收数据时那样尊重64字节的缓冲区。但是,由于JSON序列化数据的生成是空的,所以我不确定这是问题所在。

有什么想法吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

经过几次测试,我能够解决问题。

正如我上面的评论,Arduino返回空JSON的原因是来自Python的数据未正确发送。我的Python代码向Arduino发送了两个JSON字符串,但是只有第二个到达。

只有第二条消息,ArduinoJson无法解析JSON数据,从而生成了一个空的JsonObject,该JsonObject生成了一个空的JSON序列化数据(“ {}”)。

原因很明显,PySerial lib需要一些时间来打开串行端口并能够发送数据。对于这种情况,我发现不多,只有PySerial GitHub页面中报告了此issue

最终,解决方案是在打开串行端口后包括2秒的延迟。

在寻求解决方案的过程中,我更改了很多Arduino代码。

Arduino代码的主要变化:

1-更改为不使用serialEvent()。
2-包含的JsonObject.success()检查。
3-如果JsonObject.success()失败,则返回特定错误。

#include <ArduinoJson.h>

String receivedMessage = "";

void processMessage(String message) {

  const size_t bufferSize = 2 * JSON_ARRAY_SIZE(3) + JSON_OBJECT_SIZE(2) + 6 * JSON_OBJECT_SIZE(3) + 240;
  DynamicJsonBuffer jsonBuffer(bufferSize);
  //StaticJsonBuffer<bufferSize> jsonBuffer;

  JsonObject& root = jsonBuffer.parseObject(message);

  if (root.success()) {

    const int sequence = root["sequence"];
    const int state = root["state"];
    JsonArray& commands = root["commands"];
    JsonArray& statuses = root["statuses"];

    // TODO include real command/status call
    if (commands.size() > 0) {
      for (int i = 0; i < commands.size(); i++) {
        JsonObject& command = commands[i];
        command["result"] = 1;
      }
    }
    if (statuses.size() > 0) {
      for (int i = 0; i < statuses.size(); i++) {
        JsonObject& status = statuses[i];
        status["value"] = 1.1;
        status["health"] = 0;
      }
    }

    root["state"] = 0;
    root.printTo(Serial);

  } else {
    jsonBuffer.clear();
    JsonObject& error = jsonBuffer.createObject();
    error["state"] = 3;
    error.printTo(Serial);
  }

  Serial.print("<e>");

}

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
}

void loop() {

  while (!Serial.available()) {}
  receivedMessage = Serial.readString();

  receivedMessage.trim();
  receivedMessage.replace("\n", "");
  if (receivedMessage.endsWith("<e>")) {
    receivedMessage = receivedMessage.substring(0, receivedMessage.indexOf("<e>"));
    processMessage(receivedMessage);
    receivedMessage = "";
  }

}

但是解决方案是将第11行中的延迟增加到2秒:

import time

from serial import Serial


def main():

    result = ''

    serial_conn = Serial(port='COM5', baudrate=115200, timeout=0.1)
    time.sleep(2)
    serial_conn.write(str.encode('{"sequence": 0, "state": 0, "commands": [{"device_id": "1", "val'))
    serial_conn.write(str.encode('ue": 1.0, "result": 0}], "statuses": [{"device_id": "1"}]}<e>'))

    # serial_conn.flushInput()

    while True:
        # bytes_to_read = serial_conn.inWaiting()
        # msg = serial_conn.read(bytes_to_read)
        msg = serial_conn.readline()
        time.sleep(0.1)
        result += msg.decode()
        print("<{}>".format(result))
        if result.endswith('<e>'):
            break
    result = result.strip('<e>')
    print("Message received: <{}>".format(result))

    serial_conn.close()


if __name__ == '__main__':
    main()