0

I am using an Arduino to read sensors and then send the data to the serial port for Processing to read. It works totally fine when I first download the code and run the Processing program. But if I stop the Processing code and then start it again I will get the error ArrayIndexOutOfBounds. I print the array that Processing reads and it is only 3 indexes long, even though the Arduino code should be sending an array that's 7 long. This error happens because I am calling an index that doesn't exist, but it should exist, Processing just isn't receiving the entire array. If I unplug the USB cord from the Arduino and plug it back in this error won't happen and Processing will receive the entire array. I don't understand why this is happening. I have tried using the button on the Arduino to reset it each time before running the Processing code but this doesn't work, I actually need to unplug the cord and plug it back in.

If you have any input that would be great! Thanks in advance!

EDIT: I am posting the code because it was requested. There's a lot of unnecessary stuff on reading the sensors and creating graphics for in Processing. The gist is that the user interacts with a Processing program and clicks when the want the data sent by Arduino to be saved. I had been talking to someone else about the problems Ive been having and they think it may have to do with the serial buffer, and that I need to clear it or wait for it to be cleared. I have looked into this and am having trouble figuring out the proper way to do it. I have tried resetting the Arduino code using SoftRestart library, but that hasn't worked.

Here's the Arduino Code:

//need to include these libraries to be able to read from serial and read   the sensors
#include <SoftwareSerial.h>  
#include <Wire.h>
#include "Adafruit_MCP9808.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <AMS5915.h>
#include <AMS5812.h>
#include <Adafruit_ADS1015.h>

//Create sensor objects.
Adafruit_MCP9808 tempsensor18 = Adafruit_MCP9808(); //sensor with 0x18 address is Dry Bulb Temp
Adafruit_MCP9808 tempsensor19 = Adafruit_MCP9808(); //sensor with 0x19 address is Temp in chamber
Adafruit_BME280 bme; //pressure and humidity sensor
AMS5915 amsD; //sensor being used for reading differential pressure across the nozzle
AMS5812 amsS; //sensor being used to measure static pressure in the chamber
Adafruit_ADS1115 ads; //16-bit ADC sensor (actually 15-bit when used as a single-ended ADC)

//Initializing variables
float tableVals[]={0,0,0,0,0,0,0,0}; //an array that will hold sensor values. If more sensors or readings are added the size of the array needs to increase
float relHum=0;
float dryBulb=0;
float wetBulb=0;
float pressure=0;
float chamberTemp=0;
float pressureDiff;
float pressureStatic;
float rpm;
float correctedADC=0;
float correctedRPM;
float correctedADCx2=0;
float correctedRPMx2=0;
int counts=0;
const float AMS5915_0010_D_B_P_MIN = -10.0;     // minimum pressure, millibar (for sensor reading differential pressure)
const float AMS5915_0010_D_B_P_MAX = 10.0;      // maximum pressure, millibar (for sensor reading differential pressure)
const float AMS5812_0003_D_B_P_MIN = -0.3;      // minimum pressure, PSI (for sensor reading static pressure)
const float AMS5812_0003_D_B_P_MAX = 0.3;       // maximum pressure, PSI (for sensor reading static pressure)
const float AMS5812_0003_D_B_T_MIN = -25.0;     // minimum temperature, C (for sensor reading static pressure)
const float AMS5812_0003_D_B_T_MAX = 85.0;      // maximum temperature, C (for sensor reading static pressure)

//Setup function, will run one time. Initializes sensors
void setup() {
  Serial.begin(9600); //set baud rate
  amsD.getAddr_AMS5915(AMS5915_DEFAULT_ADDRESS);   // 0x28
  amsS.getAddr_AMS5812(AMS5812_DEFAULT_ADDRESS);   // 0x78
  ads.setGain(GAIN_ONE); //1x gain +/- 4.096V  1 bit = 0.125mV
  ads.begin(); //starting sensors
  amsD.begin();
  amsD.begin();
  if(!tempsensor18.begin(0x18) || !tempsensor19.begin(0x19)|| !bme.begin())    {  //wait until these sensors are ready
    while(1);
  }   
}

//loop function keeps repeating
 void loop() {
  if (counts >100){  //will only enter conditional if counts variable is over 100
    readSensors(); //go to readSensors function below

    tableVals[0]= pressure; //set pressure to the first index in array
    tableVals[1]=relHum;  //rel hum to the second index in array
    tableVals[2]=wetBulb; //wet bulb to the third index in array
    tableVals[3]=dryBulb; //dry bulb to the fourth index in array
    tableVals[4]=chamberTemp; //chamber temperature to the fifth index in the array
    tableVals[5]=pressureDiff; //differential pressure to the sixth index in the array
    tableVals[6]=pressureStatic; //static pressure to the seventh index in the array
    tableVals[7]=rpm; //RPM is the eighth index in the array
    Serial.flush();
    Serial.print(tableVals[0]); //sending all the values to Processing, need commas so Processing can split into different values (don't totally know why I put everything in an array first, that's just how I saw it done in an example)
    Serial.print(",");
    Serial.print(tableVals[1]);
    Serial.print(",");
    Serial.print(tableVals[2]);
    Serial.print(",");
    Serial.print(tableVals[3]);
    Serial.print(",");
    Serial.print(tableVals[4]);
    Serial.print(",");
    Serial.print(tableVals[5],4);
    Serial.print(",");
    Serial.print(tableVals[6],4);
    Serial.print(",");
    Serial.print(tableVals[7]);
    Serial.println(","); //need this last comma for some reason or it gets messed up
    counts=0; //reset counts variable to 0
  }
  counts++; //increment counts
}

//This function is called in the loop() function. It reads all of the sensors
void readSensors(){
  dryBulb = tempsensor18.readTempC(); //read from temp sensor with 0x18 address
  relHum = bme.readHumidity();  //read humidity from bme sensor
  pressure = bme.readPressure()/100.0F; //read pressure, in hpa
  pressure = pressure * 0.029529983071445; //convert pressure to inches of mercury
  wetBulb = dryBulb*atan(0.151977*pow(relHum + 8.313659, 0.5)) + atan(dryBulb +relHum) - atan(relHum - 1.676331) + 0.00391838*pow(relHum,1.5)*atan(0.023101*relHum) - 4.686035; //equation finds approximate wet bulb temp from humidity and dry bulb temp
  chamberTemp = tempsensor19.readTempC(); //read from temp sensor with 0x19 address   

  //this chunk of code gets the differential pressure reading and converts it to inches of water
  byte errorDiff;
  int8_t addressD;

  addressD = amsD.ams_i2cAddress;
  Wire.beginTransmission(addressD);
  errorDiff = Wire.endTransmission();
  if (errorDiff == 0){
    amsD.Measure_PressureAndTemperature(AMS5915_0010_D_B_P_MIN, AMS5915_0010_D_B_P_MAX);
    pressureDiff=amsD.getPressure();
    pressureDiff = pressureDiff * 0.40146307866177;
  }  

  //this chunk of code gets the static pressure reading and converts to inches of water
  byte errorStatic;
  int8_t addressS;

  addressS = amsS.ams_i2cAddress; 
  Wire.beginTransmission(addressS);
  errorStatic = Wire.endTransmission();
  if(errorStatic == 0){
    amsS.Measure_PressureAndTemperature(AMS5812_0003_D_B_P_MIN, AMS5812_0003_D_B_P_MAX, AMS5812_0003_D_B_T_MIN, AMS5812_0003_D_B_T_MAX);
    pressureStatic = amsS.getPressure();  
    pressureStatic = pressureStatic*27.679904842545;
  }

  //the next chunk of code takes 300 readings from the ADC, averages them together. Applies two correction equations to the ADC and then converts it to the RPM that is sent to Processing
  float ADCcount=0;
  float ADCtotal=0;
  float finalADC=0; 

  int16_t adc0;
  while(ADCcount<100){ //taking 100 ADC readings and averaging them
    adc0 = ads.readADC_SingleEnded(0);
    ADCcount=ADCcount+1;
    ADCtotal=ADCtotal+adc0;
  }

  finalADC=ADCtotal/ADCcount;
  rpm = (40740.0/32767.0)*finalADC;  
}

Processing Code:

//initializing variables
import processing.serial.*; //need this library to read serial data
Serial myPort;
int signal=0; //this will signal what part of the draw function to read
Table table;
int count =-1;
int lf = 10; //10 is the ASCII number that represents an enter key
String d_string;
String m_string;
String y_string;
float countVals[];
float pressureVals[];
String voltage=""; //these variables need to be blank strings since these are the variables that come from user input
String current=""; //they will be changed to floats before being saved in the table
String fileName="";
String COM ="";
float Pdiff;
float Pstatic;

//Set up
void setup(){
  size(300,410); //creating the start graphics
  background(255);
  String s = "Click on the rectangle to start.";
  textSize(20);
  textAlign(CENTER);
  fill(25);
  text(s,10,10, 290, 150);
  fill(255, 0, 0);
  rectMode(CORNERS);
  rect(115,85,195,160); //drawing rectangle 'button'

  table = new Table();  //create new table
  table.addColumn("Date");  //make a Date column
  table.addColumn("Reading Number"); //make a Reading Number column
  table.addColumn("Voltage V");  //Voltage column
  table.addColumn("Current A");  //Current column
  table.addColumn("Pressure inHg"); //Pressure column
  table.addColumn("Humidity %");  //Relative Humidity column
  table.addColumn("Wet Bulb Temp. *C");  //Wet Bulb Temp. column
  table.addColumn("Dry Bulb Temp. *C");  //Dry Bulb temp. column
  table.addColumn("Chamber Temp. *C"); //Chamber Temp column
  table.addColumn("Static Pressure inH20");  //Static pressure column
  table.addColumn("Differential Pressure inH2O"); //Differential Pressure column
  table.addColumn("RPM");  //RPM column

  int day = day();  //Find the day
  int month = month(); //find the month
  int year = year();  //find the year
  d_string = str(day);  //convert these integers to strings
  m_string = str(month);
  y_string = str(year); 
}

//Draw function is continuously looping
   void draw(){
      if(signal==1){ //if signal is 1 (set in keyPressed) ask the user to enter the COM
        background(255);
        String f = "Enter what number in the list of COM Ports the Arduino is plugged into (This is not necessarily the COM number.) \nPress ENTER to continue.";
        textAlign(CENTER); //center the text on the screen
        fill(25); //setting fill color
        stroke(0);  //setting stroke color
        textSize(14); //setting text size
        text(f,10,10,290,200); //displaying the instructions in the graphics box
        textSize(20); //resetting text size
        text(COM,10,125,290,200); //displaying the COM the user is typing
      }  
      else if (signal==2){ //if signal is 2 (set in keyPressed) ask the user to enter the name of the CSV file where the data will be saved
        background(255);
        String f = "Enter the name of the CSV file where the data will be saved. \nThis will be located in the folder called 'data' that is in the same folder as this application. \nPress ENTER to continue.";
        textAlign(CENTER);
        fill(25);
        stroke(0);
        textSize(12);
        text(f,10,10,290,200); //displaying instructions to the user
        textSize(20);
        text(fileName,10,125,290,200); //displaying the user file name the user is typing
      }  
      else if(signal==3){ //if signal is 3 (set in keyPressed) ask the user to input the voltage
        background(255);
        String f = "Enter the Voltage in Volts. Press ENTER to continue.";
        textAlign(CENTER);
        fill(25);
        stroke(0);
        textSize(20);
        text(f,10,10,290,200); //displaying instructions to the user
        text(voltage,10,125,290,200); //displaying the voltage the user is typing
      }  
      else if(signal ==4){ //if signal variable is 5 (set in conditional above) ask the user to input the current
        background(255);
        String f = "Enter the Current in Amps. Press ENTER to continue.";
        textAlign(CENTER);
        fill(25);
        stroke(0);
        textSize(20);
        text(f,10,10,290,200); //displaying instructions
        text(current, 10,125,290,200); //displaying the current the user is typing
      }   
       else if (signal ==5){ //if signal variable is 4 (set in mousePressed) then read the following chunk of code.
        String val=null;
        while (val==null){
          val = myPort.readStringUntil('\n'); //read serial data sent by Arduino
        }   
            val = trim(val); //trim off any white space
            countVals = float(split(val, ',')); //split serial data into individual readings
            delay(500); 
            signal =6; //set signal to 6 so next chunk of code will be read
      }         
       else if (signal ==6){ //if signal is 6 (set in the above conditional) read the following chunk of code
        TableRow newRow = table.addRow(); //add a row to the spreadsheet  
    newRow.setString("Date", m_string +"."+ d_string +"."+ y_string); //write data in the correct columns in the table
    newRow.setInt("Reading Number", count); 
    newRow.setFloat("Pressure inHg", countVals[0]);  
    newRow.setFloat("Humidity %", countVals[1]); 
    newRow.setFloat("Wet Bulb Temp. *C", countVals[2]); 
    newRow.setFloat("Dry Bulb Temp. *C", countVals[3]); 
    newRow.setFloat("Chamber Temp. *C", countVals[4]); 
    newRow.setFloat("Differential Pressure inH2O", countVals[5]);
    newRow.setFloat("Static Pressure inH20", countVals[6]);
    newRow.setFloat("Voltage V", float(voltage)); //values that were strings are converted to floats
    newRow.setFloat("Current A", float(current));
    newRow.setFloat("RPM", round(countVals[7])); 
    saveTable(table, "data/"+fileName+".csv"); //saving the data in a folder called 'data' in a file with the user given name
    signal = 7; //signal variable back to 7 to read the next conditional
  }  
  if(signal ==7){ //if signal is 7 (set in conditional above or in keyPressed), read serial port data and display graphics Draw function will continue to loop through this conditional until box is clicked so pressures are always updated.
    String pressures=null;
    while(pressures==null){
      pressures = myPort.readStringUntil('\n'); //read serial data sent by Arduino
    }
        pressures = trim(pressures); //trim off any white space
        pressureVals = float(split(pressures, ',')); //split serial data into individual readings
        println(pressureVals);
        background(255);  //reset graphics
        String s = "Click on rectangle to take a data point. Exit out of this box when done.";
        stroke(0);
        textSize(15);
        fill(25);
        textAlign(CENTER);
        text(s,10,10,290,100); //displaying instructions to user
        fill(50, 255, 200);
        rect(115,85,195,160);
        fill(0);
        textSize(12);
        textAlign(LEFT); //next line will display the previous data point if it is not the first data reading (counts needs to be 1 or greater)
        if (count>0) text("Previous Data Point: \n\t" +"Barometric Pressure inHg: " + str(countVals[0]) +"\n\tHumidity %: " +str(countVals[1]) + "\n\tWet Bulb Temp *C: " + str(countVals[2]) + "\n\tDry Bulb Temp *C: " +str(countVals[3]) +"\n\tChamber Temp *C: " + str(countVals[4])+"\n\tDifferential Pressure inH20: " + str(countVals[5]) +"\n\tStatic Pressure inH20: "+ str(countVals[6]) + "\n\tRevolutions Per Min: " + str(round(countVals[7])),10,190, 300,400);  
        textAlign(10, 400);
        text("Static: " +str(pressureVals[6]) + " in.H20", 10,400); //display the static pressure on the bottom of graphics window
        textAlign(150, 400);
        text("Diff: "+ str(pressureVals[5]) + " in.H20", 150, 400); //display the differential pressure on the bottom of graphics window      
  }  
}

//mousePressed function will run when mouse is clicked in box   
void mousePressed(){
  if(mouseX <= 195 && mouseX >= 115 && mouseY <=160 && mouseY >=85){ //if the mouse is clicked within the boundaries of the rectangle
     if(count>=0){ //if counts is greater or equal to 0 (counts starts at -1, this was b/c there were some glitches with the first click being registered)
       signal=5;
       String d= "DATA TAKEN"; //display "DATA TAKEN" to signal that a data point was taken
       textSize(15);
       fill(255,0,50);
       textAlign(LEFT);
       text(d, 107, 180);
    } 
    else{   //will run if counts is not greater than or equal to 0 (so this will just be on the first click before data is taken)
      signal=1;
    }
    count++; //increment count
    mousePressed = false;  //set mousePressed to false to make sure this function doesn't run again. 
  }
}

//keyPressed will run when a key on the keyboard is pressed
void keyPressed() {
  if(signal==1){ //if signal variable is 1...
    if(key==BACKSPACE){ //if the key pressed is BACKSPACE 
      if(COM.length()>0){ //if length of what is being typed is greater than 0
        COM = COM.substring(0,COM.length()-1);  //then get rid of the last character
      }
    }  
    else if (textWidth(COM+key) < width && key != ENTER){ //if what is being typed is not greater than the width of the graphics window and the key pressed is not ENTER
      COM = COM +key; //add the key pressed to the end of what is being typed
    }
    if(key ==ENTER){ //if ENTER is pressed
      signal=2; //change signal variable
      String portName = Serial.list()[int(COM)]; //COM is not necessarily the COM number the Arduino is plugged into. It's the number in the list of COMS that the COM is listed. I used trial and error to figure this out
      myPort = new Serial(this, portName, 9600);  //setting up baud rate
      myPort.bufferUntil(lf); //I don't know if this is actually necessary
    }
  }  
  else if(signal==2){ //if signal variable is 2...
    if(key ==BACKSPACE){ //these first two conditionals for this signal number are the same for all the signal number conditionals.
      if(fileName.length()>0){
        fileName = fileName.substring(0,fileName.length()-1);
      }
    }
    else if (textWidth(fileName+key) < width && key !=ENTER) {
      fileName = fileName +key;
    }
    if (key == ENTER) { //If key pressed is ENTER
      signal=3;  //change signal
    }
  }  
  else if(signal==3){ //If signal is 3...
    if(key ==BACKSPACE){
      if(voltage.length()>0){
        voltage = voltage.substring(0,voltage.length()-1);
      }
    }
    else if (textWidth(voltage+key) < width && key!=ENTER) {
      voltage = voltage +key;
    }
    if (key == ENTER) {
        signal=4; 
    }
  }
  else if(signal ==4){ //if signal is 4...
    if(key == BACKSPACE){
      if(current.length()>0){
        current = current.substring(0,current.length()-1);
      }
    }
    else if (textWidth(current+key) <width && key != ENTER) {
      current = current + key;
    }
    if (key ==ENTER){ 
          signal=7; //change signal to 7 and display graphics to take a data point
      key = BACKSPACE;
      background(255); 
      String s = "Click on rectangle to take a data point. Exit out of this box when done.";
      stroke(0);
      textSize(15);
      fill(25);
      text(s,10,10,290,100);
      fill(50, 255, 200);
      rect(115,85,195,160);
    }
  }  
}

Sorry, this is probably very hard to follow. I do not have that much coding experience so I'm sure my logic isn't very good.

3

I would suggest that you are seeing reset-stutter from the serial. Basically the sketch is running and filling the serial buffer with data. Then you connect and the board resets, but some data is still in the USB chip's buffer. The sketch then starts running again, and new data is sent.

In effect I am guessing you may be seeing something like:

3.87,4.45,55.13,
19.44,43.12,14.56,1.01,23.87,4.45,55.13,
19.44,43.12,14.56,1.01,23.87,4.45,55.13,
19.44,43.12,14.56,1.01,23.87,4.45,55.13,
19.44,43.12,14.56,1.01,23.87,4.45,55.13,

where the first line is left over from the previous execution before the reset. You don't get that with a complete unplug and re-plug because there is no "previous" data in the USB buffer to send.

The simplest solution is just to handle it gracefully. Take a look at the .length of your float array and see if it's correct or not. Only if it has enough entries in it do you actually want to deal with the data - otherwise, you just throw it away and wait for the next line to arrive.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.