ArduinoJson сериализация пустых данных JSON при печати последовательного вывода в программу Python - PullRequest
0 голосов
/ 11 сентября 2018

Я пытаюсь создать двустороннюю связь между платой Arduino UNO и программой Python. Формат обмена сообщениями - JSON.

Поскольку Arduino UNO имеет входной буфер в 64 байта, а сообщения JSON значительно больше этого, я реализовал способ разбивать данные JSON на сообщения в 64 байта в Python, а затем собирать их в код Arduino.

Чтобы отметить конец сообщения, я использую строку "", информирующую и Arduino, и Python о том, что сообщение было полностью доставлено.

Код Arduino работает нормально при вводе данных через Arduino Serial Monitor. JSON, который я использую в качестве входных данных, приведен ниже. Две строки, отправляемые через последовательный монитор, по одной за раз.

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

Выход для Serial Monitor следующий:

enter image description here

Но когда я запускаю тот же код Arduino с моим кодом Python, JSON, сгенерированный и отправленный обратно в Python, становится пустым ('{}').

Это мой код 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. При работе на Serial Monitor длина составляет 135, такая же длина выхода на скриншоте выше. Но при работе с Python длина равна 5, точно такой же длины "{} ", которую мы можем видеть в выводе консоли Python. Так что, очевидно, когда я работаю с Python, сериализация JSON генерирует пустые данные ("{}").

Я уже подумал, не следует ли печатать на последовательный порт (в коде Arduino), также с учетом 64-байтового буфера, как я делал при получении данных. Но поскольку сериализованные данные JSON генерируются пустыми, я не уверен, что это проблема.

Есть идеи?

Заранее спасибо!

1 Ответ

0 голосов
/ 12 сентября 2018

После нескольких испытаний я смог решить проблему.

Как прокомментировал я выше, причиной того, что Arduino возвратил пустой JSON, было то, что данные из Python были отправлены неправильно. Мой код Python отправляет две строки JSON в Arduino, но поступает только вторая.

Только со вторым сообщением ArduinoJson не удалось проанализировать данные JSON, сгенерировав пустой JsonObject, который генерировал пустые сериализованные данные JSON ("{}").

И причина этого, по-видимому, в том, что PySerial lib нужно некоторое время, чтобы открыть последовательный порт и иметь возможность отправлять данные. Я не мог найти много об этой ситуации, только эта проблема сообщена на странице PySerial GitHub.

В конце дня было решено включить задержку в 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...