Главная » 2017 » Август » 19 » Ардуино пишет письма. часть 2.1(Определение порта, Исключения, Serial detect, Exception)
02:12
Ардуино пишет письма. часть 2.1(Определение порта, Исключения, Serial detect, Exception)

Здравствуйте, продолжаем. В прошлой части мы подключали и связывали между собой Ардуино и Processing. И вероятнее всего вы заметили некоторые сложности. С некоторыми попробуем разобраться сегодня. Если вы пишите программу для себя и будете её использовать на той же машине на которой её и писали, то проблем нет. А что если вашу программу будут использовать другие. И не важно разбираются они в программировании или нет. Ведь в откомпилированном приложении уже не исправишь имя COM порта. Также нужно избавиться от порядка подключения. То есть всё должно  работать независимо от того, что мы сделали в первую очередь, подключили Ардуино или запустили приложение.  

 

Рассмотрим автоматическое определение подключения Ардуино к компьютеру. Сразу хочу сказать о том, что описанные здесь примеры не являются единственно правильными. Более того, скетч рассмотренный здесь может не корректно работать на некоторых машинах (хотя мы и старались этого избежать). Проблемы работоспособности могу быть вызваны оборудованием подключенным к компьютеру: принтеры, USB модемы, факсы, другие Ардуино и прочее. 

 

Нам нужно решить две проблемы: 

1. При запуске приложения - автоматическое определение имени COM порта к которому подключено Ардуино.

2. Автоматический поиск и подключение Ардуино. Если он (микроконтроллер) подключается после запуска приложения. Имеется ввиду физическое подключение к USB после запуска приложения.

 

Алгоритм программной реализации несложен. В начале проверим, получаем ли мы данные по сериал порту. Если НЕТ, тогда:

- получаем список доступных на нашей машине COM портов.

- подключаемся по очереди к каждому из них и проверяем наличие в нем информации от Ардуино.

- если Ардуино не подключен. Проверены все COM порты, а нужный не найден. Обновляем список доступных портов и начинаем заново, в надежде найти тот самый.

 

Приступим. Открываем среду программирования Processing и начинаем писать программу, параллельно разбираясь что к чему. Первым делом импортируем библиотеку для работы с последовательным портом. Создаем переменную temperature для хранения в ней данных с Ардуино. Это переменная строкового типа, то есть содержит "текстовую" информацию. И не смотря на то, что мы получаем в нее цифры, мы не можем производить с ней математических действий (без конвертации), потому что по сути это слово. 

Использование переменной типа String обусловлено тем, что при проверке наличия данных в COM порте, давала стабильный, ожидаемый результат. В том числе при проверке с множеством используемых / занятых, различным оборудованием, COM портов. Далее создаем объект Myport, который будем использовать как сериал соединение. 

Следующая переменная ( String[] COM; ) будет содержать список всех доступных COM портов. Такая переменная называется массив или по английски - array. В ней может храниться не одно значение, а несколько. Не будем углубляться в рассмотрение массивов. Если вы вообще не представляете себе о чем речь, представить это можно так:

Обычная переменная хранит в себе хоть и переменное, но одно значение (Пример Х = 1). Массив хранит несколько значений в разных ячейках (Пример COM = {1,54,67} и т.д.). И мы можем обращаться к ним по номерам (Пример print(COM [0] -выведет значение находящиеся в первой ячейке то есть 1.). ВНИМАНИЕ нумерация ячеек в массиве начинается с 0 (ноль). Поэтому COM[0] = 1, COM[1] = 54, COM[2] = 67

Создаем переменную USB. она будет хранить и переключать номера ячеек в массиве COM.


 

import processing.serial.*; //Импортируем библиотеку
String temperature ;//Создаем строковую переменную TEMPERATURE
Serial Myport; //Создаем объект MYPORT
String[] COM; // Создаем массив строковых данных 
int USB = 0; // Создаем целочисленную переменную USB 


Переходим к следующему куску кода. Функция SETUP. В ней мы установим размер окна приложения и заполним наш массив COM данными о доступных (на данной машине) последовательных портах. Имена портов представляют собой текст (COM1, COM2, COM3 и т.д). Поэтому нашему массиву и присвоен тип String.  


 

void setup() {//Функция стартовых установок приложения
  size(400,300); //Создаем окно приложения 400х300 пикселей
  COM = Serial.list();//Заполняем массив данными о доступных на компьютере последовательных портах
}


Далее функция DRAW. Они вместе с функциею SETUP образуют "скелет" скетча, и обязательно присутствуют в каждой программе.void draw() - это так называемый основной цикл в котором происходит все самое интересное (в зависимости от назначения и структуры скетча функция void draw() может оставаться пустой, но она обязательно должна присутствовать в скетче). На данном этапе не будем перегружать программу. Реализуем лишь вывод температуры и индикацию отсутствия подключения.  После установки соединения Ардуино и компьютера, в консоль выведем имя COM порта. У окна нашего приложения будет черный фон, установим его.

background(0);//Рисуем фон черного цвета

Далее мы проверим чему равна наша переменная, то есть получаем ли мы данные от Ардуино. Ранее мы объявили String temperature, не присвоив ей значения. Таким образом, по умолчанию (ПРИ СТАРТЕ ПРОГРАММЫ) она равна "null" или "ничего". 

if (temperature == null) {

И если условие в круглых скобках верное, а при старте это именно так, мы выполняем то что заключено в фигурные скобки. 

textSize(20);   

fill(255,10,10);  

textAlign(CENTER);   

text("СОЕДИНЕНИЕ НЕ УСТАНОВЛЕНО",200,250);   

connect();

А именно: 

textSize(20); - Устанавливаем размер шрифта (20). Он измеряется в ДИДО, как и во всех текстовых редакторах. Один ДИДО равен 0,376 мм.

fill(255,10,10); - Устанавливаем цвет текста в RGB (RED, GREEN, BLUE) формате. Составляющие формата RGB могут принимать значения от 0 до 255. Здесь мы устанавливаем практически чистый красный цвет.

textAlign(CENTER); - Устанавливаем форматирование текста относительно его начала, середины или конца. Доступно форматирование по левому краю (по умолчанию), по правому краю, по центру. Мы установили по центру.

text("СОЕДИНЕНИЕ НЕ УСТАНОВЛЕНО",200,250); - Выводим текст в координатах X,Y.

connect(); - Вызываем функцию поиска и подключения Ардуино. Это нами придуманная функция, которая реализует алгоритм поиска и подключения описанный в начале. Саму функцию разберем и напишем чуть позже.

 

А пока реализуем вариант подключенного Ардуино. То есть temperature не равно null. Наша программа выполнит следующие блок кода в фигурных скобках. Блок после else.

Иными словами мы делаем следующее:

ЕСЛИ ( if ) (температура = ничего) { пишем что соединения нет и пытаемся его установить }  ИНАЧЕ ( else ) { пишем температуру }

Мы не будем описывать блок else. Так как все операторы нам уже знакомы.


void draw() {//Основная функция. Бесконечный цикл, часто называемый ОСНОВНЫМ или ИГРОВЫМ (у геймдевов)
  background(0);//Рисуем фон черного цвета
  if (temperature == null) {// Проверяем, если переменная данных температуры не чему не равна, то...
    textSize(20);//Устанавливаем размер шрифта в 20 (размер не в пикселях, а в Дидо. 1 Дидо ~ 0,376 мм )
    fill(255,10,10);//Устанавливаем цвет заливки в RGB формате
    textAlign(CENTER);//Устанавливаем форматирование текста в центр.
    text("СОЕДИНЕНИЕ НЕ УСТАНОВЛЕНО",200,250);//Выводим сообщение в указанных координатах 
    connect();// Вызываем функцию поиска и подключения к последовательным портам.
    } else { // Иначе. Если TEMPERATURE чему-то рана, мы выводим ее на экран 
      fill(200,200,200);//Устанавливаем заливку в RGB формате
      textSize(50);//Устанавливаем размер шрифта
      textAlign(CENTER);//Центрируем текст
      text (int(temperature),200,210);//Выводим данные переменной
      textSize(25);// Устанавливаем размер шрифта
      text ("°C",200,160); //Выводим обозначение градусов Цельсия
    }
}


Функция Поиска и Подключения.

Это самая интересная и крутая часть. Начинаем с ней разбираться. И так мы помним что она (функция connect) выполняется если подключение отсутствует. В функции setup мы выполнили следующее - COM = Serial.list();

Этим нехитрым действием мы создали список (массив) всех доступных последовательных портов. И так массив это последовательность ячеек с данными. Нумерация которых начинается с ноля. Ячейки со значениями, от нулевой и до последней занятой, называются длинной массива. При обращении к ячейке, значение которой не определено (не было установлено), мы получим ошибку. При переборе всех доступных значений мы должны это учитывать и не превышать длину массива. Переменная int USB будет хранить номер ячейки массива. И перед работой с ее данными мы должны убедиться, что обращаемся к существующей ячейки.  

if (USB < COM.length) {   -  Этим условием мы проверяем что не превысили длину массива. Что ячейка к которой мы собираемся обратиться существует. И если это так, мы увеличивает значение USB на 1 (USB +=1; или так USB = USB + 1). Несколько нелогично переключать номер ячейки до того как мы поработали с ее данными. Но мы вынуждены это сделать, так как следующая часть кода будет всегда ( кроме раза, когда мы будем использовать правильное имя COM порта) выдавать системную ошибку. И операторы находящиеся в ней, не всегда будут отрабатывать.

Зная о возможной ошибке, которая возникает при неправильном имени последовательного порта. Мы будем создавать его внутри обработчика исключений. Исключением можно назвать системные ошибки. При возникновении ошибки, приложение не закроется, не зависнет, а продолжит работать. Внутри блока try находятся операторы (команды), которые могут выдать ошибку. А  блок catch (тип ошибки) отрабатывается когда ошибка происходит. В скобках указывается тип ошибки, которую мы ожидаем. В наше случае, при ошибке, мы просто отрабатываем функцию и возвращаемся в основной цикл (draw). Обратите внимание что при создании порта Myport = new Serial ( this, COM[USB-1], 9600);. Мы используем USB-1(то есть предыдущую ячейку массива) для начала проверки массива с ячейке номер ноль.

И если подключение к порту прошло без ошибок: 

- Устанавливаем условия считывания с буфера  Myport.bufferUntil('\n'); 

- Присваиваем переменной данные считанные с порта  temperature = (Myport.readStringUntil('\n'));

- Пишем в консоль имя COM порта  println(COM[USB-1]);

Если Ардуино не подключен к компьютеру. Мы переберем все COM порты и начнем с начала, обновив список доступных портов. 

else {// Значение USB равно длине массива то..      

USB = 0; //Сбрасываем значение на ноль       

COM = Serial.list();// обновляем список COM портов

После выполнения функции (однократно, если другое не предусмотрено) программа продолжает работу со следующего оператора после вызова функции. Работа с функциями будет рассмотрена более подробно. 


void connect() { // Функция поиска и подключения к последовательному порту
    if (USB < COM.length) {// Если значение переменной USB меньше длинны массива, то...
      USB +=1;// Увеличиваем значение USB на 1
      try {// Это обработчик исключений. Приложение продолжает работать даже при "СИСТЕМНОЙ ОШИБКЕ"
      Myport = new Serial ( this, COM[USB-1], 9600); //Пытаемся подключится к выбранному порту
      Myport.bufferUntil('\n');//Устанавливаем значение функции serialEvent
      temperature = (Myport.readStringUntil('\n'));
      println(COM[USB-1]);// Выводим в консоль имена проверенных СОМ портов 
      } catch (Exception e) {// Указываем тип системной ошибки (ИСКЛЮЧЕНИЯ). В нашем случае любой тип
      //Здесь указывается что выполняется при ошибке. Мы просто продолжаем работать
      }
    } else {// Значение USB равно длине массива то..
      USB = 0; //Сбрасываем значение на ноль 
      COM = Serial.list();// обновляем список COM портов
    }
}


Ниже приведен листинг целиком. Здесь мы не стали рассматривать функцию serialEvent. Она не сложная и рассматривалась в прошлых частях. 


import processing.serial.*; //Импортируем библиотеку
String temperature ;//Создаем строковую переменную TEMPERATURE
Serial Myport; //Создаем объект MYPORT
String[] COM; // Создаем массив строковых данных 
int USB = 0; // Создаем целочисленную переменную USB 

void setup() {//Функция стартовых установок приложения
  size(400,300); //Создаем окно приложения 400х300 пикселей
  COM = Serial.list();//Заполняем массив данными
}

void draw() {//Основная функция. Бесконечный цикл
  background(0);//Рисуем фон черного цвета
  if (temperature == null) {// Проверяем переменную.
    textSize(20);//Устанавливаем размер шрифта
    fill(255,10,10);//Устанавливаем цвет заливки в RGB формате
    textAlign(CENTER);//Устанавливаем форматирование текста в центр
    text("СОЕДИНЕНИЕ НЕ УСТАНОВЛЕНО",200,250);//Выводим сообщение 
    connect();// Вызываем функцию поиска и подключения
    } else { // Иначе 
      fill(200,200,200);//Устанавливаем заливку в RGB формате
      textSize(50);//Устанавливаем размер шрифта
      textAlign(CENTER);//Центрируем текст
      text (int(temperature),200,210);//Выводим данные переменной
      textSize(25);// Устанавливаем размер шрифта
      text ("°C",200,160); //Выводим обозначение градусов Цельсия
    }
}

void serialEvent (Serial Myport) {
  temperature = (Myport.readStringUntil('\n'));
}

void connect() { // Функция поиска и подключения
    if (USB < COM.length) {// Проверяем что не достигнут конец массива
      USB +=1;// Увеличиваем значение USB на 1
      try {// Это обработчик исключений
      Myport = new Serial ( this, COM[USB-1], 9600); //Пытаемся подключится
      Myport.bufferUntil('\n');//Устанавливаем знак для serialEvent
      temperature = (Myport.readStringUntil('\n'));// присваиваем данные 
      println(COM[USB-1]);// Выводим в консоль имя СОМ порта 
      } catch (Exception e) {// Указываем тип системной ошибки (ИСКЛЮЧЕНИЯ)
      //Здесь указывается что выполняется при ошибке
      }
    } else {// Значение USB равно длине массива то..
      USB = 0; //Сбрасываем значение на ноль 
      COM = Serial.list();// обновляем список COM портов
    }
}


Продолжение следует. В следующих частях будем отправлять письма, создавать интерфейс, будет интересно.

Следующая часть. И исходник альфы. (Альфа - это версия приложения на стадии разработки. Сырой но работоспособный проект.)

В форуме создана тема по данному циклу статей. Вопросы и пожелания оставляйте там. 

Категория: ЖЕЛЕЗО/ПРОГРАММИРОВАНИЕ. | Просмотров: 771 | Добавил: phom1 | Теги: serial detect, программирование ардуино, ардуино, исключения processing, автоопределение КОМ порта, Exception
Всего комментариев: 0
Добавить комментарий