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.

1 Like

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:

1 Like

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;
}
1 Like