prat
prat

Reputation: 103

Sending JSON over serial in Python to Arduino

I've been trying to set up this sample snippet for communicating over Serial with Python. I intend to send a simple JSON packet and de-serialise it over the other end with an Arduino.

But the JSON Library taken from here throws an error of Invalid Input.

#include <ArduinoJson.h>

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

void loop() {
  int     size_ = 0;
  char    payload[256];
  String  string = "";
  if (size_ = Serial.available()) {
  for(int i =0; i < size_; i++) {
     payload[i] = Serial.read();
     string += payload[i];   
  }

  const size_t capacity = 1024;
  DynamicJsonDocument doc(capacity);
  DeserializationError error = deserializeJson(doc, payload);
  if (error) {
    Serial.println(error.c_str());
    return;
  }
    if(doc["operation"] == "ACK") {
      Serial.println("ACK has been received");
    }
    else {
      Serial.println("Error!");
    }
  }
  while (!Serial.available()) {
  }
  delay(20);
}

This is the python snippet.

import time
import json
import serial
import pprint


if __name__ == "__main__":
    print ("Ready...")
    data = {}
    data["operation"] = "ACK"
    ser = serial.Serial("COM3", baudrate= 115200, timeout=0.5)
    data = json.dumps(data)
    if ser.isOpen(): 
        (ser.write(bytes(data, "utf-8"), ))
        try:
            incoming = ser.readline().decode("utf-8")
            print ((incoming))
        except Exception as e:
            print (e)
        ser.close()
    else:
        print ("opening error")

For some reason I can't figure out why, when python sends over the chunk it doesn't seem to recognise the input.

Is it because of the 'b' added infront of it? Is it because of the encoding method used?

When i try to input {"operation":"ACK"} via the terminal, it recognises it perfectly, and sends back a success message.

Why isn't it working over python? [ I am using Python 3 ]

[UPDATE] I tried to change a bit of the python code to do some debugging.

if __name__ == "__main__":
    print ("Ready...")
    data = {}
    data["operation"] = "ACK"
    ser = serial.Serial("COM3", baudrate= 115200, timeout=1)
    data = json.dumps(data)
    buf = []

    if ser.isOpen():
        for x in (data):
            buf.append(ord(x))

        ser.write(bytearray(buf))
        try:
            incoming = ser.readline()
            for x in incoming:
                print ((x))
        except Exception as e:
            print (e)
        ser.close()
    else:
        print ("opening error")

and changed the Arduino script to send back the string it received.

#include <ArduinoJson.h>

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

void loop() {
  int     size_ = 0;
  char    payload[256];
  String  string = "";
  while (size_ = Serial.available()) {
  for(int i =0; i < size_; i++) {
     payload[i] = Serial.read();
     string += payload[i];   
  }

  const size_t capacity = 1024;
  DynamicJsonDocument doc(capacity);
  DeserializationError error = deserializeJson(doc, payload);
  if (error) {
    //Serial.println(error.c_str());
    Serial.print(string.length());
    for (int i = 0; i < string.length() ; i++) {
      Serial.write(string[i]);
    }
    return;
  }
    if(doc["operation"] == "ACK") {
      Serial.println("ACK has been received");
    }
    else {
      Serial.println("Error!");
    }
  }
  while (!Serial.available()) {
  }
  delay(20);
}

This is the python output.

Ready...

50
50
240
240
123
34
111
112
101
114
97
116
105
111
110
34
58
32
34
65
67
75
34
125

As you can see the size is printed twice right after the "ready..." and there are two chars of value 240 sent from the Arduino. I don't understand why there are these two chars in the front.

Upvotes: 3

Views: 9674

Answers (1)

prat
prat

Reputation: 103

After a while of figuring out what the problem was, There were couple of issues.

  • The baud-rate was too fast, I had to reduce it down to 9600.

  • The encoding from python was supposed to be sent as ASCII.

  • On Arduino reading the string until '\n' was needed.
  • Statically allocating the JsonDocument to 512 bytes worked.

    The arduino JSON-Assistant has a method of finding out the require allocation for the architecture used.

The python code below.

import time
import json
import serial
from pprint import pprint
import random

if __name__ == "__main__":
    print ("Ready...")
    ser  = serial.Serial("COM3", baudrate= 9600, 
           timeout=2.5, 
           parity=serial.PARITY_NONE, 
           bytesize=serial.EIGHTBITS, 
           stopbits=serial.STOPBITS_ONE
        )
    data = {}
    data["operation"] = "sequence"

    data=json.dumps(data)
    print (data)
    if ser.isOpen():
        ser.write(data.encode('ascii'))
        ser.flush()
        try:
            incoming = ser.readline().decode("utf-8")
            print (incoming)
        except Exception as e:
            print (e)
            pass
        ser.close()
    else:
        print ("opening error")

The arduino code.

#include <ArduinoJson.h>
void setup() {
  Serial.begin(9600); 
  while(!Serial) {
  }
}

void loop() {
  int     size_ = 0;
  String  payload;
  while ( !Serial.available()  ){}
  if ( Serial.available() )
    payload = Serial.readStringUntil( '\n' );
  StaticJsonDocument<512> doc;

  DeserializationError   error = deserializeJson(doc, payload);
  if (error) {
    Serial.println(error.c_str()); 
    return;
  }
  if (doc["operation"] == "sequence") {
     Serial.println("{\"Success\":\"True\"}");
  }
  else {
      Serial.println("{\"Success\":\"False\"}");
   }
  delay(20);
}

Hope someone finds this useful.

Upvotes: 7

Related Questions