Serial port communication (json encoding decoding)

Hello I'm writing a program which would send information about cpu usage and free ram memory to arduino and arduino would display it on screen. Program is written with python and to send info I'm using json to encode whole info and send it to arduino, but I don't know how to decode that info on arduino to display it on lcd.... Maybe someone can help?
By far my arduino code was like this:

#include <LiquidCrystal.h>

//Set's lcd to the Arduino's ports
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);

void setup() {
  lcd.begin(16, 2);
Serial.begin(9600);

}

void loop() {
 
  String content = "";
  char character;
 

  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
 
  }

  if (content != "") {
    Serial.println(content);
    lcd.print(content);
  }
  if (content == ""){
    lcd.print("Duomenu nera");}
  delay(1000);
  lcd.clear();
  
}

and to send info I'm using json to encode whole info

Why? Why not send the data in a form that is easy to parse?

OK, this is my python code:

import psutil, time, sys, serial
import math
import json
z = 0
y = 0
x = 0
port = raw_input("COM port : ")
ser = serial.Serial(port, 9600)
while True:
    try:
        time.sleep(1)
        z = psutil.cpu_percent()
        x = psutil.virtual_memory()
        y = x.free
        y = y/math.pow(1024,2)
        data = { 'cpu':z,
	 'ram':y}
        ser.write(str(data))
        print 'RAM free', (int(y)), 'mb'
        print 'CPU usage', (int(z)), '%'
        print json.dumps(data)
    except KeyboardInterrupt:
            print 'Done'
            ser.close()
            sys.exit(0)

and this is what I made with arduino:

#include <JsonParser.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);
using namespace ArduinoJson::Parser;

void setup() {
  lcd.begin(16, 2);
Serial.begin(9600);
}

void loop() {
 
  String content = "";
  char character;
 
  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
  } 

  if (content != "") {
    char json [] = content;
    JsonParser<8> parser;
    JsonObject root = parser.parse(json);
    int cpu    = root["cpu"];
    int ram    = root["ram"];
    Serial.println(cpu);
    Serial.println(ram);
    lcd.setCursor(0,0);
    lcd.print("CPU usage: ");
    lcd.print(cpu);
    lcd.setCursor(0,1);
    lcd.print("RAM free: ");
    lcd.print(ram);
  }
  if (content == ""){
    lcd.print("Duomenu nera");
  }
  delay(1000);
  lcd.clear();
  
}

I get an error which says :
error: initializer fails to determine size of 'json'

I don't know the better way to send this data because I want to decode it and show it on LCD like this:

CPU usage: **%
RAM free: **%

if i would send just z & y to arduino without encode I believe it could not do it because it would be printent as one content? If you know easier way please tell me :((

Like @PaulS I can't see the point of using JSON for communication with an Arduino.

...R

So what would you recommend to use? Or how to do this?

        data = { 'cpu':z,
	 'ram':y}

What does this look like when it gets to the serial port?

Store the data in a NULL terminated array of chars, and use strtok() to parse the data. Of course, it will help to know what the data looks like on the Arduino side.

And, ditch the stupid String class.

manic9:
So what would you recommend to use? Or how to do this?

Develop your parsing code using the serial monitor. You may need to figure out an easier way to package the data from the pc before sending it. Do you have an example of the actual data string sent fom the pc?

The readCSV() function in this demo shows how to parse data on the Arduino. There are similar capabilities in Python.

This demo shows simple comms between an Arduino and a PC using Python.

This demo shows how to send binary data from a PC using Python - doing it the other way would be similar.

...R

Thank you all for help! @Robin2 your readCSV() function helped a lot :slight_smile:

Ok last question, the readCSV() function that Robin2 showed me in his code takes a string(text), an integer and a float and seperates them, in my case I need that it only take two numbers sent as strings seperated by kommas, I tried to modificate code, but now it prints ram perfectly and do not print CPU, what is wrong?

Here's the arduino code:

#include <LiquidCrystal.h>

LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);


const byte buffSize = 32;
char inputSeveral[buffSize]; // space for 31 chars and a terminator

byte maxChars = 12; // a shorter limit to make it easier to see what happens
                           //   if too many chars are entered

int cpu = 0;
int ram = 0;
char inputCsvString[12];




void setup() {
Serial.begin(9600);
lcd.begin(16, 2);

}

void loop() {
  readCSV();

}


void readCSV() {

      // this function expects a series of comma separated values
      // for this demo the sequence of items must be a string, an integer, a float
      // for example testing, 123 , 4567.89
      // spaces around the commas are optional
   
        // first read severalChars into the array inputSeveral

    inputSeveral[0] = 0;
    maxChars = buffSize - 1; // use full size of buffer for this function
    byte charCount = 0;  
    byte ndx = 0;        
    
    if (Serial.available() > 0) {
      while (Serial.available() > 0) { 
        if (ndx > maxChars - 1) {
          ndx = maxChars;
        } 
        inputSeveral[ndx] = Serial.read();
        ndx ++;        
        charCount ++;
      }
      if (ndx > maxChars) { 
        ndx = maxChars;
      }
      inputSeveral[ndx] = 0; 
   
   
    // now we need to split the received string into its parts
      // this is done by strtok() which looks for the token - the comma in this case

    char *  partOfString; // this is used by strtok() as an index
       
    
    partOfString = strtok(inputSeveral,","); 
    cpu = atoi(partOfString);     // convert this part to an integer
    
    partOfString = strtok(NULL, ","); 
    ram = atoi(partOfString);     // convert this part to a float
    
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("CPU: ");
    lcd.print(cpu);
    lcd.print(" %");
    lcd.setCursor(0,1);
    lcd.print("RAM: ");
    lcd.print(ram);
    lcd.print(" mb");
    delay(1000);
   
 
   
   
 
  }
  
  else {
    lcd.clear();
    lcd.setCursor(0,0);
  lcd.print("DUOMENU NERA");
  
  }
  
  
  
 

    
  
}

Python code here:

import psutil, time, sys, serial
import math
z = 0
y = 0
x = 0
port = raw_input("COM port : ")
ser = serial.Serial(port, 9600)
while True:
    try:
        time.sleep(1)
        z = psutil.cpu_percent()
        x = psutil.virtual_memory()
        y = x.free
        y = y/math.pow(1024,2)
        data = (z, y)
        ser.write(str(data))
        print 'RAM free', (int(y)), 'mb'
        print 'CPU usage', (int(z)), '%'
    except KeyboardInterrupt:
            print 'Done'
            ser.close()
            sys.exit(0)

Can you print the contents of inputSeveral immediately before you start trying to parse it?

Have you tried sending data to your revised program using the Serial Monitor.

It might be a good idea to change

if (Serial.available() > 0) {

to reflect the number of characters you are expecting - if the length will always be the same.

In this demo the process of receiving data is a little different - the data is surrounded by start- and end-markers so the Arduino knows it has received everything. That means it can receive a variable number of characters with certainty.

It's difficult to know what to include in each demo to avoid confusing people with too much info.

...R

If I do it like this in python program
data = ("test", z, y) // where z = cpu and y= ram
Ser.write(str(data))

Then I can do everything like in your demo because I send text and two numbers, but if I remove "test" it doesn't work with changes that I made and prints only Ram, and leaving cpu = 0; I will try to change what you just said, but I don't know what will be the exact lengh of message because it changes every second...

manic9:
If I do it like this in python program

Maybe you could detect all three parts in your Arduino sketch and just ignore the first item?

More significantly, you failed to answer the main question I asked - printing the contents of inputSeveral. I don't understand how you expect to solve the problem without looking at the data that is causing it. The Arduino may not be receiving what was sent by Python.

...R

Is z really being sent as an int? Doesn't look to me like it is.

Arduino is receiving full message that is send over python program I have tried to print it as one content it worked. Yeah I have already done that arduino is ignoring the first one and printing second and third item and it works perfectly, just I wanted to make program a little shorter cutting off unnecesary pieces.

Below is a simple data parsing setup you might consider.

//zoomkat 11-12-13 String capture and parsing  
//from serial port input (via serial monitor)
//and print result out serial port
//copy test strings and use ctrl/v to paste in
//serial monitor if desired
// * is used as the data string delimiter
// , is used to delimit individual data 

String readString; //main captured String 
String angle; //data String
String fuel;
String speed1;
String altidude;

int ind1; // , locations
int ind2;
int ind3;
int ind4;
 
void setup() {
  Serial.begin(9600);
  Serial.println("serial delimit test 11-12-13"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like 90,low,15.6,125*
  //or 130,hi,7.2,389*

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == '*') {
      //do stuff
      
      Serial.println();
      Serial.print("captured String is : "); 
      Serial.println(readString); //prints string to serial port out
      
      ind1 = readString.indexOf(',');  //finds location of first ,
      angle = readString.substring(0, ind1);   //captures first data String
      ind2 = readString.indexOf(',', ind1+1 );   //finds location of second ,
      fuel = readString.substring(ind1+1, ind2+1);   //captures second data String
      ind3 = readString.indexOf(',', ind2+1 );
      speed1 = readString.substring(ind2+1, ind3+1);
      ind4 = readString.indexOf(',', ind3+1 );
      altidude = readString.substring(ind3+1); //captures remain part of data after last ,

      Serial.print("angle = ");
      Serial.println(angle); 
      Serial.print("fuel = ");
      Serial.println(fuel);
      Serial.print("speed = ");
      Serial.println(speed1);
      Serial.print("altidude = ");
      Serial.println(altidude);
      Serial.println();
      Serial.println();
      
      readString=""; //clears variable for new input
      angle="";
      fuel="";
      speed1="";
      altidude="";
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

manic9:
Arduino is receiving full message that is send over python program I have tried to print it as one content it worked.

This is like pulling teeth.

How about showing me exactly what is in inputSeveral so I can try to figure out why your mod to my program is not working.

...R