Добрый день. Нарыл в инете пример, как считывать данные с ИК дальномера и пытаюсь его повторить с небольшими своими доработками. Программу на компе делаю на процессинге. Наткнулся на ошибку 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.
Не могли бы вы ещё код для самой Ардуины показать? Я не мастер по java, но с Proccesing дело имел и делал разные плюшки с ним. Компилятор ругает вас за хард код: Код (Text): servoPos = int(posString); //Ещё неизвестно, какое значение примет servoPos! По умолчанию = null ... distances[servoPos] = distance; Нельзя так делать. Переменная servoPos ещё сама неизвестна, а компилятору откуда знать её значение? При сборке программы компилятор не может угадать какого размера будет массив. Это только первое, что в глаза бросилось и, как я считаю, есть ещё нюансы. Дословно: NullPointerException - "исключение нулевого указателя" Т.к. servoPos ещё не имеет своего значение, он принял его по умолчанию - null, т.е ничего. Компилятор не может создать массив размером null, т.е. размером с ничего P.S Задайте вручную размер массива, исходя от размера и кол-ва данных, хранимых в нем.
Вот код ардуино: Код (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. Но как его проверить в процессинге не знаю.
Обнаружил вот какой странный момент. При вызове distances.lenght тоже выскакивает эта ошибка. Я сейчас работаю на ноуте с Win8. Может в этом проблема? Читал, что у процессинга какие-то нестыковки с операционками мелкомягких. То в 7 что-то отваливалось, а теперь, видимо и с 8 проблемы. Вчера пытался firmata запустить. На ардуину залил без проблем, а вот процессинговая часть не работает. При инициализации возвращает какую-то длинную ошибку. Такое ощущение что processing в Win8 работает некорректно.
Итак. Решился-таки инициализировать массив вручную. Т.е. тупо прописал 181 значение в строке объявления массива. Убрал из процедуры setup его инициализацию и заполнение. Оно заработало. Ошибки нет. Получается, что ошибка про нулпоинтер связана не с индексом, а самим массивом. Это его указатель оказался нулом. Т.е. этот указатель неверно передался в другие процедуры, хотя он и объявлен как глобальный. Кто-нибудь из гуру может как-то прокомментировать данную ситуацию?
Я не гуру, но отвечу по очереди: Иначе не как. (имхо) Конечно отработает, ведь 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...
Код (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];, тогда будет происходить обращение к глобальной переменной, а не создание локальной.