Программа чтения данных из ардуины

Тема в разделе "Микроконтроллеры AVR", создана пользователем Spell, 6 июл 2015.

  1. Spell

    Spell Нерд

    Добрый день.
    Нарыл в инете пример, как считывать данные с ИК дальномера и пытаюсь его повторить с небольшими своими доработками.
    Программу на компе делаю на процессинге.
    Наткнулся на ошибку java. Не знаю, что с этим делать.
    Вот в чем проблема.
    Ардуина через USB передает значение угла поворота сервомашинки и значение дальномера.
    В приемной программе эти данные считываются и заполняются в массив.
    Вот код процессинга:
    Код (Text):

    int numAngles = 181;
    //variables for data to graph
    int servoPos;
    int distance;

    int[] distances; //distance readings for each angle are stored here

    void setup() {
      size(800, 400);
      smooth();
      noStroke();

      servoPos = 0;
      prevPos = 0;

      // List all the available serial ports:
      println(Serial.list());

      int[] distances = new int[numAngles];
     
      for (int i = 0; i < numAngles; i++)
      {
      distances = 0;
      //print("i:");
      //print(i);
      //print(" val:");
      //println(distances);
      }

      // I know that the first port in the serial list on my machine
      // is always my Arduino, so I open Serial.list()[0].

      // Open whatever port is the one you're using.
      myPort = new Serial(this, Serial.list()[1], baudRate);

      myPort.bufferUntil(lf);

      //set up the display text which will display the data recieved via serial
      myFont = createFont(PFont.list()[2], fontSize);
      textFont(myFont);

      noLoop(); //don't redraw unless serial data so draw() will not loop.

    }

    void serialEvent(Serial p) {
      int delimIndex = -1;
      String inString;
      String posString;
      String disString;

      //return;
      try { //just in case something goes wrong with the serial we wrap all of
      //this with a try statement, so the whole program will not die.

      inString = (myPort.readString());
      //print("inString: ");
      //println(inString);
      delimIndex = inString.indexOf(','); //find first comma
      if (delimIndex != -1) { //if we found first comma
      posString = inString.substring(0, delimIndex); //parse servo pulse
      disString = inString.substring(delimIndex+1, inString.length()-2); //length()-2 <- strips off the linefeed
     
      //print("pos: ");
      //print(posString);
      //print(" dist: ");
      //println(disString);
     
      distance = int(disString);
      servoPos = int(posString); //convert from nasty stringses to nice intses.

      print("pos: ");
      print(servoPos);

      print(" dist: ");
      println(distance);
      //print(" | Dist: ");
      //println(distances[servoPos]);
     
      distances[servoPos] = distance; //use the step to index the array of distances
      //print("Pos: ");
      //print(servoPos);
      //print(" | Dist: ");
      //println(distances[servoPos]);
     
      //subtracting 1 to account for zero indexed array of course.
      redraw(); //update display; calls draw()
      }
      }
      catch(Exception e) { //DOH! something broke
      print("Error: ");
      println(e); //print description of badness to console.
      }
     
    }
     

    На выделенной строке вылезает ошибка:
    java.lang.NullPointerException
    Как я понял, получается некорректное обращение к массиву.
    Массив distances объявлен как int.
    Переменная servoPos тоже int. При чтении из порта ее значение получается из конвертации строки в int.
    При этом в setup обращение distances в цикле проходит, а обращение distances[servoPos] возвращает ошибку java.lang.NullPointerException.
    Я пытался найти что-то по этому поводу в инете, написано много. Но я пока не силён в java, поэтому не очень понял. Понял только, что, видимо, каким-то образом переменная servoPos превратилась в null. Но где и как, не понимаю.

    Подскажите, что я делаю не так?

    Если важно, то процессинг у меня 2.2.1.
     
    Последнее редактирование: 9 июл 2015
  2. CryNET

    CryNET Гик

    Не могли бы вы ещё код для самой Ардуины показать?

    Я не мастер по java, но с Proccesing дело имел и делал разные плюшки с ним.

    Компилятор ругает вас за хард код:
    Код (Text):

    servoPos = int(posString); //Ещё неизвестно, какое значение примет servoPos! По умолчанию = null
    ...
    distances[servoPos] = distance;
    Нельзя так делать. Переменная servoPos ещё сама неизвестна, а компилятору откуда знать её значение? При сборке программы компилятор не может угадать какого размера будет массив.

    Это только первое, что в глаза бросилось и, как я считаю, есть ещё нюансы.

    Дословно: NullPointerException - "исключение нулевого указателя"

    Т.к. servoPos ещё не имеет своего значение, он принял его по умолчанию - null, т.е ничего.
    Компилятор не может создать массив размером null, т.е. размером с ничего :)

    P.S
    Задайте вручную размер массива, исходя от размера и кол-ва данных, хранимых в нем.
     
  3. Spell

    Spell Нерд

    Вот код ардуино:
    Код (Text):

    #include <Servo.h>

    //serial
    #define BAUDRATE 9600 //38400

    //panning control defines
    #define NUM_STEPS 17   //number of steps to pan servo
    #define READWAIT 150   //number of milliseconds to wait before reading IR sensor on panning servo

    //input pins
    #define FRONT_IR_PIN 0

    //output pins
    #define SERVO_PIN 9

    Servo myservo;  // create servo object to control a servo
                    // twelve servo objects can be created on most boards

    int pos = 0;    // variable to store the servo position
    int IRVal; //ir sensor value

    void setup()
    {
      myservo.attach(SERVO_PIN);  // attaches the servo on pin 9 to the servo object
      Serial.begin(BAUDRATE);
    }

    void loop()
    {
      SetAngel(myservo, 0);
      delay(1000);
      SetAngel(myservo, 180);
      delay(1000);
    }

    void SetAngel(Servo dServo, int dAngel)
    {
      int curPos = dServo.read();

      //Serial.print("SetAngel:  dAngel = ");
      //Serial.print(dAngel);
      //Serial.print("  curPos = ");
      //Serial.println(curPos);
      if (curPos == dAngel)
      {
        return;
      };
      if (curPos > dAngel)
      {
        for(pos = curPos; pos >= dAngel; pos -= 1) // goes from 0 degrees to 180 degrees
        {                                  // in steps of 1 degree
          dServo.write(pos);              // tell servo to go to position in variable 'pos'
          delay(10);                       // waits 15ms for the servo to reach the position
          ReadValue();
        }
      }

      //if (curPos < dAngel)
      else
      {
        for(pos = curPos; pos <= dAngel; pos += 1) // goes from 0 degrees to 180 degrees
        {                                  // in steps of 1 degree
          dServo.write(pos);              // tell servo to go to position in variable 'pos'
          delay(10);                       // waits 15ms for the servo to reach the position
          ReadValue();
        }
      };
      //delay(1000);
    }

    void ReadValue()
    {
      IRVal = analogRead(FRONT_IR_PIN);
      Serial.print(pos);
      Serial.print(",");
      Serial.println(IRVal);
    }

    Не уверен, что в нем есть проблема. Он вполне нормально возвращает текстовую строку в порт.
    Проверял через терминал.

    Исходник для процессинга брал вот отсюда: http://www.uchobby.com/index.php/2009/03/08/visualizing-sensor-with-arduino-and-processing/
    Там действительно массив заполняли вручную. Но там его размер сильно меньше, и меня как-то ломает писать 180 нулей. Хотя, если совсем припрет, сделаю. Правда, не думаю, что поможет.
    Тем более, что код из процедуры setup отрабатывает без проблем. Т.е. и массив создался и заполнился.
    Код (Text):

    int[] distances = new int[numAngles];
    for (int i = 0; i < numAngles; i++)
    {
    distances[i] = 0;
    //print("i:");
    //print(i);
    //print(" val:");
    //println(distances[i]);
    }
     

    Если раскомментировать строки с print, то они будут все замечательно выводить. Т.е. обращение distances работает без ошибок.

    Насчет неопределенности servoPos тоже как-то не уверен.
    Вот кусок кода:
    Код (Text):

    distance = int(disString);
    servoPos = int(posString); //convert from nasty stringses to nice intses.

    print("pos: ");
    print(servoPos);

    print(" dist: ");
    println(distance);
    //print(" | Dist: ");
    //println(distances[servoPos]);

    distances[servoPos] = distance; //use the step to index the array of distances
     

    Обратите внимание, что после получения значения servoPos идет вызов функции print с этим значением. И она не валится, а возвращает вполне разумное значение от 0 до 180. Что и должно быть.
    А вот вызов строки println(distances[servoPos]); (которая сейчас закомментирована) уже возвращает ошибку.
    Я грешу на то, что после преобразования у переменной servoPos тип все-таки не int. Но как его проверить в процессинге не знаю.
     
    Последнее редактирование: 9 июл 2015
  4. Spell

    Spell Нерд

    Обнаружил вот какой странный момент.
    При вызове distances.lenght тоже выскакивает эта ошибка.
    Я сейчас работаю на ноуте с Win8. Может в этом проблема?
    Читал, что у процессинга какие-то нестыковки с операционками мелкомягких. То в 7 что-то отваливалось, а теперь, видимо и с 8 проблемы.
    Вчера пытался firmata запустить. На ардуину залил без проблем, а вот процессинговая часть не работает. При инициализации возвращает какую-то длинную ошибку.
    Такое ощущение что processing в Win8 работает некорректно.
     
  5. Spell

    Spell Нерд

    Итак.
    Решился-таки инициализировать массив вручную. Т.е. тупо прописал 181 значение в строке объявления массива.
    Убрал из процедуры setup его инициализацию и заполнение.
    Оно заработало. Ошибки нет.

    Получается, что ошибка про нулпоинтер связана не с индексом, а самим массивом. Это его указатель оказался нулом. Т.е. этот указатель неверно передался в другие процедуры, хотя он и объявлен как глобальный.

    Кто-нибудь из гуру может как-то прокомментировать данную ситуацию?
     
  6. CryNET

    CryNET Гик

    Я не гуру, но отвечу по очереди:
    Иначе не как. (имхо)
    Конечно отработает, ведь numAngles уже известна: int numAngles = 181;
    Код (Text):

    int numAngles = 181;
    int[] distances = new int[numAngles]; //тоже самое int[] distances = new int[181];
    for (int i = 0; i < numAngles; i++)
    {
    distances[i] = 0; //вы заполняете массив нулями, который уже имеет 181 "ячеек".
    }
    Все известно, нету никаких null переменных.
    Смотря какая, скорее всего массив был меньше, чем значение servoPos...
     
  7. Megakoteyka

    Megakoteyka Оракул Модератор

    Код (Text):
    int[] distances; //distance readings for each angle are stored here

    void setup() {
      size(800, 400);
      smooth();
      noStroke();

      servoPos = 0;
      prevPos = 0;

      // List all the available serial ports:
      println(Serial.list());

      int[] distances = new int[numAngles];
      ...
    }
    Первая переменна distances глобальна и с ней программа пытается работать.
    Но в setup объявляется своя собственная переменная distances и инициализируется именно она, а не глобальная. Поэтому глобальная остается =null.
    Напишите distances = new int[numAngles];, тогда будет происходить обращение к глобальной переменной, а не создание локальной.
     
    Последнее редактирование: 10 июл 2015
    CryNET нравится это.
  8. Spell

    Spell Нерд

    Спасибо.
    Проверил - действительно работает.
    Надо запомнить.
     
    Последнее редактирование: 10 июл 2015