Serial Event null pointer exception

I get this error code every few times:

COM4
test
{"lat{"lat":67.74801,"lng":21.2934}

java.lang.RuntimeException: Expected a ':' after a key
	at processing.data.JSONObject.<init>(JSONObject.java:264)
	at processing.data.JSONObject.<init>(JSONObject.java:226)
	at processing.core.PApplet.parseJSONObject(PApplet.java:6065)
	at processed.serialEvent(processed.java:68)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
test
34}

	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at processing.serial.Serial.serialEvent(Unknown Source)
	at jssc.SerialPort$EventThread.run(SerialPort.java:1112)
java.lang.RuntimeException: A JSONObject text must begin with '{'
	at processing.data.JSONObject.<init>(JSONObject.java:242)
	at processing.data.JSONObject.<init>(JSONObject.java:226)
	at processing.core.PApplet.parseJSONObject(PApplet.java:6065)
	at processed.serialEvent(processed.java:68)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
test
{"lat":64.74801,"lng":18.2934}


This is my Processing sketch:

import processing.serial.*;
import java.util.List;
import java.util.ArrayList;

String val;     // Data received from the serial port
Serial serialConnectionObject;
int serialChannelIndexInt;
float lat;
float lng;
int rectSizeX = 40 ;
int rectSizeY = 40;
float latcoord = 0;
float longcoord = 0;
boolean newData = false;
float xCoord, yCoord, oldxCoord, oldyCoord;
List<deltaXY> coordinates;
float x, y, traceX, traceY;
deltaXY dXY;

void setup()
{
  size(400,400);
  background(0);
  InitSerialConnectionVoid(0);
  String portName = Serial.list()[serialChannelIndexInt]; //change the 0 to a 1 or 2 etc. to match your port
  println(portName);
  serialConnectionObject.bufferUntil('\n');
  x = 0.5 * width;
  y = 0.5 * height;
  dXY = new deltaXY();
}

void draw()
{   
  if(newData) {
    fill(#FFFFFF, 10);
    noStroke();
    rectMode(CENTER);
    r(coordinates.size()-1);
  }
}

void serialEvent(Serial p) {
  try {
    println("test");
      val = p.readString();
        println(val);
      JSONObject json = parseJSONObject(val);
      if (json == null) {
        newData = false;
      } else {
        println(json.toString());
        lat = json.getFloat("lat");
        lng = json.getFloat("lng");
        println(lat);
        oldxCoord = xCoord;
        oldyCoord = yCoord;
        LatLngtoXY(lat,lng);
        dXY.setDX(xCoord, oldxCoord);
        dXY.setDY(yCoord, oldyCoord);
        coordinates.add(dXY);
        if (coordinates.size() >=100) {
          coordinates.remove(0);
        }
        newData=true;
      }
      //latcoord = map(lat, lat-0.00045000045, lat+0.00045000045, 0, width);
      //longcoord = map(lng, lng-0.00045000063, lng+0.00045000063, 0, height);
      
      
    }
  catch(RuntimeException e) {
     e.printStackTrace(); 
  }
    
}

void r(int i) {
  if (i==0) {
    fill(#FFFFFF, 10);
    noStroke();
    rectMode(CENTER);
    rect(x, y, rectSizeX, rectSizeY);
  } else {
    float dx = coordinates.get(i).getDX();
    float dy = coordinates.get(i).getDY();
    traceX += dx;
    traceY += dy;
    fill(#FFFFFF, 50);
    noStroke();
    //rectMode(CENTER);
    rect(x+traceX, y+traceY, rectSizeX, rectSizeY);  
    println(traceX,traceY);
    r(i-1);
  }
}

void LatLngtoXY(float lat, float lon) {
    float mapWidth    = width;
    float mapHeight   = height;
    
    // get x value
    xCoord = (lon+180)*(mapWidth/360);
    
    // convert from degrees to radians
    float latRad = lat*PI/180;
    // get y value
    float mercN = log(tan((PI/4)+(latRad/2)));
    yCoord     = (mapHeight/2)-(mapWidth*mercN/(2*PI));
}

void InitSerialConnectionVoid(int _serialChannelIndexInt){
 
    serialChannelIndexInt = _serialChannelIndexInt;
 
    if(serialChannelIndexInt == Serial.list().length){ return; }
    try{
 
        serialConnectionObject = new Serial(this, Serial.list()[serialChannelIndexInt], 115200);
        serialConnectionObject.bufferUntil('\n');
 
    }
    catch (RuntimeException e){
 
        serialConnectionObject = null;
        println(e);
        println("SERIAL CONNECTION FAILED");
        InitSerialConnectionVoid(serialChannelIndexInt + 1);
 
    }
 
}

class deltaXY {
  float dx;
  float dy;

  deltaXY() {
  }

  public void setDX(float xCoord, float oldxCoord) {
    this.dx = xCoord-oldxCoord;
  }  
  public void setDY(float yCoord, float oldyCoord) {
    this.dy = yCoord-oldyCoord;
  }  
  public float getDX() {
    return this.dx;
  }  
  public float getDY() {
    return this.dy;
  }
}

And this is my Arduino sketch:

#include <ArduinoJson.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
TinyGPSPlus gps;
SoftwareSerial ss(RXPin, TXPin);

const int capacity=JSON_OBJECT_SIZE(2);
StaticJsonDocument<capacity> doc;

float a = 48.748010;
float b = 2.2934;

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

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

void loop()
{  
  doc["lat"]=a;
  doc["lng"]=b;
  a = a+1;
  b= b+1 ;
  /*
 
  if(gps.location.isValid()==true) {
    doc["lat"] = gps.location.lat();
  }
  else {
    doc["lat"] = "*";
  }

 
  if(gps.location.isValid()==true) {
    doc["lng"] = gps.location.lng();
  }
  else {
    doc["lng"] = "*";
  }
  */
  serializeJson(doc, Serial);
  Serial.write( '\n');
 
  smartDelay(1000);
}

I wonder whether this may have to do with delays? How can I avoid null pointers except with try catch? the exceptions happens too often and nothing is drawn on screen since after a few exceptions it stops.

The Arduino serial monitor shows that JSON is stored correctly. It just isn’t properly processed by Serial event.

You’re invoking method remove() inside serialEvent() callback, which is run by the Serial’s Thread btW! :thread:

That is a big no, no! You’re better off just using readString() inside serialEvent() to grab the received String. :prayer_beads:

However, parse that as JSONObject within draw() instead. :framed_picture:

You can use the noLoop()/redraw() approach for a much easier event management: :bulb:

Thanks a lot! I understand what you said, and I think I implemented some improvements. However, now I get nullpointerexceptions on the call to r(coordinates.size()-1);
Can I not use the method to draw in such a way?

Here is my current sketch:

import processing.serial.*;
import java.util.List;
import java.util.ArrayList;

String val;     // Data received from the serial port
Serial serialConnectionObject;
int serialChannelIndexInt;
float lat;
float lng;
int rectSizeX = 40 ;
int rectSizeY = 40;
float latcoord = 0;
float longcoord = 0;
boolean newData = false;
float xCoord, yCoord, oldxCoord, oldyCoord;
List<deltaXY> coordinates;
float x, y, traceX, traceY;
deltaXY dXY;
JSONObject json;

void setup()
{
  size(400,400);
  background(0);
  InitSerialConnectionVoid(0);
  String portName = Serial.list()[serialChannelIndexInt]; //change the 0 to a 1 or 2 etc. to match your port
  println(portName);
  serialConnectionObject.bufferUntil('\n');
  x = 0.5 * width;
  y = 0.5 * height;
  dXY = new deltaXY();
  noLoop();
}

void draw()
{   try {
        json = parseJSONObject(val);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    if (json == null) {
        newData = false;
      } else {
        println(json.toString());
        lat = json.getFloat("lat");
        lng = json.getFloat("lng");
        println(lat);
        oldxCoord = xCoord;
        oldyCoord = yCoord;
        LatLngtoXY(lat,lng);
        dXY.setDX(xCoord, oldxCoord);
        dXY.setDY(yCoord, oldyCoord);
        try {
        coordinates.add(dXY);
        }
        catch(Exception e) {
          e.printStackTrace();
        }
        fill(#FFFFFF, 10);
        noStroke();
        rectMode(CENTER);
        r(coordinates.size()-1);
        if (coordinates.size() >=100) {
              coordinates.remove(0);
        }
        //newData=true;
      }

}



void serialEvent(Serial p) {
  try {
    println("test");
     val = p.readString();
     println(val);
     if(val != null) {
      redraw();
     }
  }
      
   catch(RuntimeException e) {
     e.printStackTrace(); 
  }
    
}

void r(int i) {
  if (i==0) {
    fill(#FFFFFF, 10);
    noStroke();
    rectMode(CENTER);
    rect(x, y, rectSizeX, rectSizeY);
  } else {
    float dx = coordinates.get(i).getDX();
    float dy = coordinates.get(i).getDY();
    traceX += dx;
    traceY += dy;
    fill(#FFFFFF, 50);
    noStroke();
    //rectMode(CENTER);
    rect(x+traceX, y+traceY, rectSizeX, rectSizeY);  
    println(traceX,traceY);
    r(i-1);
  }
}

void LatLngtoXY(float lat, float lon) {
    float mapWidth    = width;
    float mapHeight   = height;
    
    // get x value
    xCoord = (lon+180)*(mapWidth/360);
    
    // convert from degrees to radians
    float latRad = lat*PI/180;
    // get y value
    float mercN = log(tan((PI/4)+(latRad/2)));
    yCoord     = (mapHeight/2)-(mapWidth*mercN/(2*PI));
}

void InitSerialConnectionVoid(int _serialChannelIndexInt){
 
    serialChannelIndexInt = _serialChannelIndexInt;
 
    if(serialChannelIndexInt == Serial.list().length){ return; }
    try{
 
        serialConnectionObject = new Serial(this, Serial.list()[serialChannelIndexInt], 115200);
        serialConnectionObject.bufferUntil('\n');
 
    }
    catch (RuntimeException e){
 
        serialConnectionObject = null;
        println(e);
        println("SERIAL CONNECTION FAILED");
        InitSerialConnectionVoid(serialChannelIndexInt + 1);
 
    }
 
}

class deltaXY {
  float dx;
  float dy;

  deltaXY() {
  }

  public void setDX(float xCoord, float oldxCoord) {
    this.dx = xCoord-oldxCoord;
  }  
  public void setDY(float yCoord, float oldyCoord) {
    this.dy = yCoord-oldyCoord;
  }  
  public float getDX() {
    return this.dx;
  }  
  public float getDY() {
    return this.dy;
  }
}

Before anything else, you should get rid of all those moot try/catch () blocks.

Its need is rare in Processing, b/c most exceptions are simply swallowed up by the API.

So your serialEvent() callback should be as plain simple as this:

void serialEvent(final Serial s) {
  val = s.readString().trim();
  redraw();
}

Now, back to r(coordinates.size() - 1);, I wonder where you initialize the List coordinates variable?

I could only spot this statement and no new for it: List<deltaXY> coordinates;

You should replace that w/: final List<deltaXY> coordinates = new ArrayList<deltaXY>();.

Also, you should consider initializing your String val variable w/ something which could be parsable as a JSONObject.

For example, something like this: String val = "{}";.

It’s important to do so b/c very likely draw() is gonna be called back much before serialEvent() would have a chance to initialize val w/ the received data.

Well, I’ve adapted my old sketch “Efficient Serial Reading” to get a JSON data and store it in a Point2D.Float container as a simplified example:
Docs.Oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/geom/Point2D.Float.html

Given I don’t have the hardware to test it myself, it’s up to you to check it out whether it works or not: :flushed:

// Discourse.Processing.org/t/serial-event-null-pointer-exception/13275/4
// GoToLoop (2019/Aug/09)

import processing.serial.Serial;
import java.awt.geom.Point2D;

static final int PORT_INDEX = 0, BAUDS = 115200;
final Point2D.Float xy = new Point2D.Float();
String myString = "{}"; // represents an empty json object

void setup() {
  size(400, 400);
  noLoop();

  stroke(#FFFF00);
  strokeWeight(1.5);

  final String[] ports = Serial.list();
  printArray(ports);
  new Serial(this, ports[PORT_INDEX], BAUDS).bufferUntil(ENTER);
}

void draw() {
  println(frameCount + ": " + myString);
  final JSONObject json = parseJSONObject(myString);
  println(json);

  if (json == null || json.size() < 2)  return;
  final float x = json.getFloat("lat"), y = json.getFloat("lng");

  clear();
  if (frameCount > 2)  line(xy.x, xy.y, x, y);
  else                 point(x, y);

  xy.setLocation(x, y);
  println(xy);
}

void serialEvent(final Serial s) {
  myString = s.readString().trim();
  redraw = true;
}