Обработка, отправленная Arduino, не считывает целые массивы, если шнур не отключен и не подключен снова?

Я использую Arduino для считывания датчиков, а затем отправляю данные на последовательный порт для считывания Processing. Все работает отлично, когда я сначала загружаю код и запускаю программу Processing. Но если я останавливаю код Processing и затем снова запускаю его, то получаю ошибку ArrayIndexOutOfBounds. Я печатаю массив, который считывает Processing, и он имеет длину всего 3 индекса, хотя код Arduino должен отправлять массив длиной 7. Эта ошибка происходит, потому что я вызываю индекс, который не существует, но он должен существовать, Processing просто не получает весь массив. Если я отключу USB-кабель от Arduino и снова подключу его, эта ошибка не возникнет, и Processing получит весь массив. Я не понимаю, почему это происходит. Я пробовал использовать кнопку на Arduino, чтобы сбрасывать его каждый раз перед запуском кода Processing, но это не работает, мне на самом деле нужно отсоединить шнур и снова подключить его.

Если у вас есть какие-либо предложения, это было бы здорово! Спасибо заранее!

РЕДАКТИРОВАНИЕ: Я публикую код, потому что его запросили. В нем много ненужной информации о считывании показаний датчиков и создании графиков для Processing. Суть в том, что пользователь взаимодействует с программой Processing и нажимает кнопку, когда хочет сохранить данные, отправленные Arduino. Я говорил с кем-то о проблемах, с которыми я столкнулся, и они думают, что это может быть связано с последовательным буфером, и что мне нужно очистить его или подождать, пока он очистится. Я изучил это и не могу понять, как это сделать правильно. Я пробовал сбросить код Arduino с помощью библиотеки SoftRestart, но это не сработало.

Вот код Arduino:

//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;  
}

Код обработки:

//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);
    }
  }  
}

Извините, это, вероятно, очень сложно понять. У меня не так много опыта в кодировании, поэтому я уверен, что моя логика не очень хороша.

, 👍0


1 ответ


Лучший ответ:

3

Я бы предположил, что вы видите сброс-заикание от последовательного порта. В основном скетч работает и заполняет последовательный буфер данными. Затем вы подключаетесь, и плата сбрасывается, но некоторые данные все еще находятся в буфере чипа USB. Затем скетч снова начинает работать, и отправляются новые данные.

По сути, я предполагаю, что вы можете увидеть что-то вроде:

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,

где первая строка осталась от предыдущего выполнения перед сбросом. Вы не получите этого при полном отключении и повторном подключении, потому что в буфере USB нет «предыдущих» данных для отправки.

Самое простое решение — просто изящно с этим справиться. Взгляните на .length вашего массива float и проверьте, правильный он или нет. Только если в нем достаточно записей, вы действительно хотите иметь дело с данными — в противном случае вы просто выбрасываете их и ждете прибытия следующей строки.

,