Map function worked in Processing 2 and early 3.. But broke in 3.5.3. Need guidance

Hi. This exact code worked in Processing versions 2 and and earlier version of 3. I upgraded to 3.5.3 . Since upgrading my HP laptop to Processing 3.5.3 I now get the error:
(note: I have another laptop still running Processing 2.0 and this exact code works fine on it. Just tried 3.4 with same problem)

“map(NaN, 0, 1023, 100, 1080) called, which returns NaN (not a number)
Error, disabling serialEvent() for COM16
null”

Code reads serial data. Aurdino is sending a value between 1 and 999, typically 200 - 400…

It looks like the variable inByte is not recognized as an intiger in ver 3.5.3. See code:

/******************************************************************************
/******************************************************************************
Heart_Rate_Display.ino
Demo Program for AD8232 Heart Rate sensor.
Casey Kuhns @ SparkFun Electronics
6/27/2014
https://github.com/sparkfun/AD8232_Heart_Rate_Monitor

The AD8232 Heart Rate sensor is a low cost EKG/ECG sensor.  This example shows
how to create an ECG with real time display.  The display is using Processing.
This sketch is based heavily on the Graphing Tutorial provided in the Arduino
IDE. http://www.arduino.cc/en/Tutorial/Graph

Resources:
This program requires a Processing sketch to view the data in real time.

Development environment specifics:
  IDE: Arduino 1.0.5
  Hardware Platform: Arduino Pro 3.3V/8MHz
  AD8232 Heart Monitor Version: 1.0

This code is beerware. If you see me (or any other SparkFun employee) at the
local pub, and you've found our code helpful, please buy us a round!

Distributed as-is; no warranty is given.
******************************************************************************/

import processing.serial.*;

Serial myPort;        // The serial port
int xPos = 1;         // horizontal position of the graph
float height_old = 0;
float height_new = 0;
float inByte = 0;


void setup () {
  // set the window size:
  size(1900, 400);        

  // List all the available serial ports
  println(Serial.list());
  // Open whatever port is the one you're using.
  myPort = new Serial(this,Serial.list()[0], 9600);
  // don't generate a serialEvent() unless you get a newline character:
  myPort.bufferUntil('\n');
  // set inital background:
  background(0xff);
}


void draw () {
  // everything happens in the serialEvent()
}


void serialEvent (Serial myPort) {
  // get the ASCII string:
  String inString = myPort.readStringUntil('\n');

  if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);

    // If leads off detection is true notify with blue line
    if (inString.equals("!")) { 
      stroke(0, 0, 0xff); //Set stroke to blue ( R, G, B)
      inByte = 512;  // middle of the ADC range (Flat Line)
    }
    // If the data is good let it through
    else {
      stroke(0xff, 0, 0); //Set stroke to red ( R, G, B)
      inByte = float(inString); 
     }
     
     //Map and draw the line for new data point
     inByte = map(inByte, 0, 1023, 100, height);
     height_new = height - inByte; 
     line(xPos - 1, height_old, xPos, height_new);
     height_old = height_new;
    
      // at the edge of the screen, go back to the beginning:
      if (xPos >= width) {
        xPos = 0;
        background(0xff);
      } 
      else {
        // increment the horizontal position:
        xPos++;
      }
    
  }
}

//import processing.serial.*;
1 Like
  • Mutating the sketch’s main canvas outside the “Animation Thread” crashes the program! :boom:
  • Callback serialEvent() is run by the Serial’s Thread btW. :warning:
  • So even calling something like background() can be fatal! :fearful:
  • Actually, due to a bad design, any Processing function which depends on color() will get wrong results when they’re multithreading invoked! :bug:
  • So any canvas mutating operations should be moved to draw() or called by it. :face_with_monocle:
  • Here’s a post link for a bare minimum Serial data receiver demo: :angel:
/**
 * Efficient Serial Reading (v1.02)
 * GoToLoop (2016-Jan-19)
 *
 * Forum.Processing.org/two/discussion/14534/
 * myport-available-always-0#Item_1
 *
 * Forum.Processing.org/two/discussion/16618/
 * processing-with-arduino-void-serialevent#Item_5
 *
 * Discourse.Processing.org/t/
 * map-function-worked-in-processing-2-and-early-3-
 * but-broke-in-3-5-3-need-guidance/11371/2
 */
 
import processing.serial.Serial;
 
static final int PORT_INDEX = 0, BAUDS = 9600;
String myString = "";
 
void setup() {
  noLoop();
  final String[] ports = Serial.list();
  printArray(ports);
  new Serial(this, ports[PORT_INDEX], BAUDS).bufferUntil(ENTER);
}
 
void draw() {
  println(myString);
}
 
void serialEvent(final Serial s) {
  myString = s.readString().trim();
  redraw = true;
}
1 Like

Hi GoToLoop - That certainly is more efficient! Thanks. But it doesn’t solve the invalid map issue. I’m at a loss to get my ECG monitor working again. It’s kind of important to me :). I can goback to very 2.0, but that isn’t “really” a longterm solution because I need recent versions to learn. I hope to port this to android so I can run it via bluetooth on my phone.
Thanks!

EDIT! I just saw the other important notes you added in the bullet points. I will attempt to put code in the correct places… I didn’t write it, and am a novice… But will give it the old college try.

Ho GoToLoop:
2019-05-19T04:00:00Z
Thank you for your guidance. I’ve rearranged the code so that (I think?) everything is where it is supposed to be. It WORKS! Also managed to up the speed from 9600bps to 38400 bps which improved the resolution. I appreciate that you gave direction, but not the actual revised/working code. This was a great learning experience for me.
Be well and good things to you!

BEGIN new revision that works:

import processing.serial.*;

Serial s;        // The serial port
int xPos = 1;         // horizontal position of the graph
float height_old = 0;
float height_new = 0;
float inByte = 0;
static final int PORT_INDEX = 0, BAUDS = 38400;
String hrNumber = "";


void setup () {

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

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

    if (hrNumber != null) {
      // trim off any whitespace:
      hrNumber = trim(hrNumber);
      println(hrNumber);
    }
  }

void draw () 
{
//  background(0xff);
  if (hrNumber.equals("!")) { 
    stroke(0, 0, 0xff); //Set stroke to blue ( R, G, B)
    inByte = 200;  // middle of the ADC range (Flat Line)
  }
  // If the data is good let it through
  else {
    stroke(0xff, 0, 0); //Set stroke to red ( R, G, B)
    inByte = float(hrNumber);
  }

  //Map and draw the line for new data point
  inByte = map(inByte, 0, 1023, 0, height);
  height_new = height - inByte; 
  line(xPos - 1, height_old, xPos, height_new);
  height_old = height_new;

  // at the edge of the screen, go back to the beginning:
  if (xPos >= width) {
    xPos = 0;
    background(0xdd);
  } else {
    // increment the horizontal position:
    xPos = xPos += 2;
  }
}
1 Like

You’re using trim() 2x: :flushed:

  1. hrNumber = s.readString().trim();
  2. hrNumber = trim(hrNumber);

You can delete the 2nd trim() statement safely. :wink:

And I’ve also spotted you’re checking for null at if (hrNumber != null) {. :see_no_evil:

If it were even possible for Serial::readString() to return null inside callback serialEvent(), calling trim() after s.readString() would had already crashed the program, before reaching your if (hrNumber != null) {. :grimacing:

The reason why Serial::readString() can’t return null is b/c, by default, serialEvent() is only called back after a byte had arrived. :flight_arrival:

Specifically in your case, when an ENTER (\n) character is received: .bufferUntil(ENTER);. :nerd_face:

Therefore, Serial::readString() would always return a String w/ 1 character at the very least. :money_mouth_face:

1 Like

GoToLoop,
You’re exactly correct! I removed the entire IF statement and placed the println in the body of void serialEvent right under redraw = true.

Thank you so much for helping me learn this.
Here is what I believe to be the final code:

import processing.serial.*;

Serial s;        // The serial port
int xPos = 1;         // horizontal position of the graph
float height_old = 0;
float height_new = 0;
float inByte = 0;
static final int PORT_INDEX = 0, BAUDS = 38400;
String hrNumber = "";


void setup () {

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

  void serialEvent(final Serial s) {
    hrNumber = s.readString().trim();
    redraw = true;
    println(hrNumber);

  }

void draw () 
{
//  background(0xff);
  if (hrNumber.equals("!")) { 
    stroke(0, 0, 0xff); //Set stroke to blue ( R, G, B)
    inByte = 200;  // middle of the ADC range (Flat Line)
  }
  // If the data is good let it through
  else {
    stroke(0xff, 0, 0); //Set stroke to red ( R, G, B)
    inByte = float(hrNumber);
  }

  //Map and draw the line for new data point
  inByte = map(inByte, 0, 1023, 0, height);
  height_new = height - inByte; 
  line(xPos - 1, height_old, xPos, height_new);
  height_old = height_new;

  // at the edge of the screen, go back to the beginning:
  if (xPos >= width) {
    xPos = 0;
    background(0xdd);
  } else {
    // increment the horizontal position:
    xPos = xPos += 2;
  }
}

I can’t see how to mark a topic as resolved…

I don’t think this forum got such feature. :woozy_face:

BtW, you should edit your posts and fix your code format, so others can study it. :warning:

Select your posted code and hit SHIFT + CTRL + C. Or even easier, click at the </> icon. :cowboy_hat_face:

@ItchyBrain you can click in the pen symbol on the post you want to edit. Then you can follow the instructions provided by Goto above to format your code. If something is not clear about formatting your code, let me know as I can help.

Kf

GoTo and Frajer:

I edited all my posts to place code in the code boxes and added the final code to the last post.
Thank you for your patience and not booting me for bad forum behavior…

Best Regards,

2 Likes