Главная » Статьи » Arduino с нуля » Arduino и Processing

PDE. Урок 3. Изменение размеров, положения и поведения объектов на экране

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

В этом уроке мы воспользуемся полученными знаниями, и создадим небольшую графическую программу, которая будет выводить некие объекты, реагирующие на изменение значений потенциометров. 

 

Общая часть

В настоящем уроке мы соберем устройство на платформе Arduino, используя при этом потенциометры. Создадим графическую программу, в которой будем изменять размеры, положение и поведение объекта на экране, меняя сопротивления резисторов. 

 

Для урока нам понадобятся следующие компоненты:

 

  1. Arduino Uno
  2. Breadboard
  3. Соединительные провода "папа-папа"
  4. Перемычки
  5. Потенциометры 3шт.

 

Теперь нужно собрать схему устройства. На протяжении всего урока схема будет одинаковая. Она будет состоять из трех потенциометров. Изменять в уроке мы будем только интерфейс графической программы. 

 

Собираем схему нашего устройства:

 

У меня получилось так:

 

 

 

И заливаем этот скетч в Arduino:

 

int R = A1;
int G = A2;
int B = A3;

void setup()
{
 
 Serial.begin (9600);

}

void loop() 
{
 int val_1 = map(analogRead(R),0,1023,0,255);
 Serial.println(val_1);
 delay(50); 
 
 int val_2 = map(analogRead(G),0,1023,256,511);
 Serial.println(val_2);
 delay(50); 
 
 int val_3 = map(analogRead(B),0,1023,512,767);
 Serial.println(val_3);
 delay(50); 

}

 

Схема и скетч Вам должны быть знакомы с самого первого урока по Processing'у. Если Вы не понимаете работу кода, советую прочесть этот урок: "PDE. Урок 1. Первые шаги к освоению".

 

Пример 1

Arduino мы настроили. Теперь пора немного поработать с визуализацией наших данных, получаемых с потенциометров. 

Давайте создадим на экране некий объект, напоминающий наш резистор. Он будет состоять из окружности (колпачка) и метки, которая будет указывать в каком положении находится потенциометр. 

 

Основой для нашей программы будет служить этот код:

import processing.serial.*; 
Serial port;


float ob1 = 0; //первый потенциометр
float ob2 = 0; //второй потенциометр
float ob3 = 0; //третий потенциометр


void setup()
{
 size (300,300);
 port = new Serial(this, "COM3",9600);
 port.bufferUntil('\n');
}

void draw()
{

}

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 }
}

 

Т.к. этот код мы разбирали в предыдущих уроках, на нем останавливаться подробно не будем. Основное, что надо знать, это то, что в функции serialEvent() мы считываем значения наших сопротивлений, которые поступают на вход COM порта с Arduino. И переписываем эти значения, присваивая переменным ob1, ob2, ob3, в диапазоне от 0 до 5. 

Таким образом, основа для нашей программы готова. Теперь будем писать в функции draw() те действия, которые будут выполняться с изменением сопротивлений резисторов.  

 

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

void draw()
{

 //создаем область для размещения потенциометров
 noStroke(); //убираем контур области
 fill(#625B5B); //делаем заливку 
 rect(10,10,280,280); //рисуем область с отступами по 10px с каждой стороны
 
}

Для наглядности, делаем заливку области отличную от основного фона программы. Получаем следующее:

 

 

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

Теперь в этой области нужно расположить модель нашего потенциометра. Для этого добавляем окружность:

void draw()
{

 //создаем область для размещения потенциометров
 noStroke(); //убираем контур области
 fill(#625B5B); //делаем заливку 
 rect(10,10,280,280); //рисуем область с отступами по 10px с каждой стороны
 
//------------------------------/ 
/*--- Создание потенциометра --*/
//------------------------------/

 //рисуем основной колпачок
 stroke(0); //делаем контур колпачка черным
 fill(0); //заливаем фигуру черным цветом
 ellipse(150,150,100,100); //рисуем окружность в центре окна
 
}

 

Добавили окружность черного цвета в центр окна, с диаметром 100px. Это и будет основа колпачка нашего потенциометра. Получили следующее:

 

 

Теперь нужно добавить центральную ось и метку положения. Для этого переписываем код:

void draw()
{

 //создаем область для размещения потенциометров
 noStroke(); //убираем контур области
 fill(#625B5B); //делаем заливку 
 rect(10,10,280,280); //рисуем область с отступами по 10px с каждой стороны
 
//------------------------------/ 
/*--- Создание потенциометра --*/
//------------------------------/

 //рисуем основной колпачок
 stroke(0); //делаем контур колпачка черным
 fill(0); //заливаем фигуру черным цветом
 ellipse(150,150,100,100); //рисуем окружность в центре окна
 
 //рисуем ось колпачка
 fill(255); //заливаем белым цветом
 ellipse(150,150,30,30); //рисуем окружность в центре окна
 
 //создаем метку на потенциометре
 strokeWeight(10); //толщина линии
 stroke(255); //заливка линии белая
 line(150,150,200,150); //рисуем линию
 
}

 

Получили вот такую программу:

 

 

Конечно это все схематично. Чтобы просто настроить программу. Когда программа будет работать, можно будет изменить вид потенциометра, сделать его более правдоподобным. Но сейчас, для понимания всего процесса, и этого будет достаточно. 

Как Вы наверное уже поняли, если мы будем изменять сопротивление резисторов, то на экране ничего происходить не будет. Все потому, что мы в функции draw() не использовали получаемые значения с Arduino. 

 

Давайте это исправим! Чтобы нам казалось, что потенциометр на экране вращается, нужно изменять положение метки. Точнее ее конечные координаты (x1 и y1), которые находятся на краю колпачка. 

Как определить эти самые конечные координаты? Нужно вспомнить тригонометрию. Чтобы было более понятно, посмотрите на этот рисунок:

 

x0 и y0 это начальные координаты нашего колпачка. H - та самая метка (представляет собой линию). Начальные координаты совпадают с центром колпачка, а конечные координаты (x1 и y1) располагаются на окружности. Их и нужно найти. 

Подробно останавливаться на этом не будем. Чтобы найти координаты, нужно воспользоваться следующими формулами:

 

х1 = x0 + (cos(angle) * radius)
у1 = y0 + (sin(angle) * radius)

 

Где angle - угол, который мы и записываем в переменные ob1-ob3. Radius -радиус нашего колпачка (в данном случае, диаметр колпачка равен 100px, значит радиус будет равен 50px). x0 и y0 - начальные координаты (в данном случае x0=y0=150).

 

Теперь давайте добавим эти формулы в наш скетч:

import processing.serial.*; 
Serial port;

float ob1 = 0; //первый потенциометр
float ob2 = 0; //второй потенциометр
float ob3 = 0; //третий потенциометр

void setup()
{
 size (300,300);
 port = new Serial(this, "COM3",9600);
 port.bufferUntil('\n');
}

void draw()
{

 //создаем область для размещения потенциометров
 noStroke(); //убираем контур области
 fill(#625B5B); //делаем заливку 
 rect(10,10,280,280); //рисуем область с отступами по 10px с каждой стороны
 
//------------------------------/ 
/*--- Создание потенциометра --*/
//------------------------------/

 //рисуем основной колпачок
 stroke(0); //делаем контур колпачка черным
 fill(0); //заливаем фигуру черным цветом
 ellipse(150,150,100,100); //рисуем окружность в центре окна
 
 //рисуем ось колпачка
 fill(255); //заливаем белым цветом
 ellipse(150,150,30,30); //рисуем окружность в центре окна
 
 //Вычисляем конечные координаты метки
 float x1 = 150 + (cos(ob1)*50); //координата x1
 float y1 = 150 + (sin(ob1)*50); //координата y1

 //создаем метку на потенциометре
 strokeWeight(10); //толщина линии
 stroke(255); //заливка линии белая
 line(150,150,x1,y1); //рисуем линию
 
}

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 }
}

 

Вот и все. Мы задействовали один потенциометр для изменения поведения объекта в программе. Результат программы будет такой:

 

 

Пример 2

Давайте теперь задействуем все три потенциометра. Выведем на экран три переменных резистора, каждый резистор будет отвечать за свой цвет, а фон окна будет соответствовать сумме всех трех цветов. 

 

Основу кода мы возьмем из предыдущего примера, только немного изменим. В функции draw() мы создавали наше рабочее поле, здесь же нам это не понадобится, т.к будем заливать постоянно все окно. Воспользуемся функцией background(). Аргументы функции будут все три составляющие цвета (color_R, color_G, color_B), изменяемые нашими потенциометрами. 

Их мы считываем с Arduino в функции serialEvent():

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 color_R = map(val,0,255,0,255);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 color_G = map(val,256,511,0,255);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 color_B = map(val,215,767,0,255);
 }
}

 

Т.к мы используем новые переменные, необходимо их объявить. Мы это делаем в самом начале программы, перед функцией setup(). Также нужно записать в функцию draw() функцию заливки экрана, получаем следующий код:

import processing.serial.*; //импорт Serial библиотеки
Serial port; //Идентификация Serial порта с именем "port"

//Считываемые значения
float ob1 = 0; //первый потенциометр
float ob2 = 0; //второй потенциометр
float ob3 = 0; //третий потенциометр

float color_R = 0; //красный цвет
float color_G = 0; //зеленый цвет
float color_B = 0; //синий цвет

void setup()
{
 size (500,200);
 port = new Serial(this, "COM3",9600);
 port.bufferUntil('\n');
}

void draw()
{
 background(color_R,color_G,color_B); //делаем заливку как сумма всех цветов
}

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 color_R = map(val,0,255,0,255);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 color_G = map(val,256,511,0,255);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 color_B = map(val,215,767,0,255);
 }
}

 

Если мы сейчас запустим программу, то получим такое окно:

 

 

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

Можно просто взять кусок кода из предыдущего примера, где создавался один потенциометр и копировать его для двух других, изменив аргументы функций (угол, координаты). Но этот способ нерациональный, т.к используя такой метод, мы делаем код более громоздким. В таких случаях, лучше всего использовать функции. Поэтому давайте создадим новую функцию, которая получив какие-то начальные данные, сама создаст потенциометр. 

 

Создаем функцию, potenz() (создается за фигурными скобками других функций) :

void potenz (float x0, float y0, int r,int g, int b, float ug)
{ 

}

Аргументы, которые функция будет принимать:

  • x0 - начальная координата потенциометра по x
  • y0 - начальная координата потенциометра по y
  • r - цвет потенциометра
  • g - цвет потенциометра
  • b - цвет потенциометра
  • ug - значение с Arduino

Т.е, например, если мы захотим нарисовать потенциометр с начальными координатами по x и y 200, с заливкой белого цвета (r = 255, g = 255 ,b = 255), и зависящий от второго потенциометра, то мы напишем в функции draw() следующее:

 potenz(200,200,255,255,255,ob2);

 

Только пока ничего не произойдет. Нужно описать все действия с аргументами в функции potenz(). 

Создаем колпачок, метку и ось. Тут Вы можете сами немного поэкспериментировать. Сделать внешний вид потенциометра на свое усмотрение. Я же, для примера, предлагаю такой вариант:

void potenz (float x0, float y0, int r,int g, int b, float ug)
{ 
 //рисуем основной колпачок
 strokeWeight(1); //толщина линии
 stroke(0); //делаем контур колпачка черным
 fill(#434242);//заливаем фигуру
 ellipse(x0,y0,100,100); //рисуем окружность с центром в координатах x0 y0
 
 //создаем грань
 stroke(r,g,b); //делаем контур r,g,b цветом
 fill(#747070); //заливка фигуры
 ellipse(x0,y0,80,80); //рисуем окружность
 
 //рисуем центральную ось
 fill(r,g,b); //заливаем фигуру r,g,b цветом
 ellipse(x0,y0,30,30); //рисуем окружность
 
 //Вычисляем конечные координаты метки
 float x1 = x0 + (cos(ug)*50); //координата x1
 float y1 = y0 + (sin(ug)*50); //координата y1


 //создаем метку на потенциометре
 strokeWeight(10); //толщина линии
 stroke(r,g,b); //заливка линии
 line(x0,y0,x1,y1); //рисуем линию
 
}

 

Как Вы видите, в коде присутствуют наши аргументы (x0,y0,r,g,b,ug). Эти переменные мы будем задавать при вызове функции, и от них будет зависеть вид и положение объекта на экране.

Функция создания потенциометра готова, теперь нужно ее вызвать. Для этого переходим в функцию draw().

Параметры цвета, для функции potenz() (r,g,b), Вы можете взять из "Color Selector" на панели, во вкладке "Tools":

 

 

Создаем три потенциометра, задав для каждого из них свои начальные координаты и цвет. А также, каждый из потенциометров, зависит от своего резистора (ob1,ob,ob3):

 //отображаем первый потенциометр
 potenz(100,100,255,10,39,ob1);
 
 //отображаем второй потенциометр
 potenz(250,100,6,191,41,ob2);
 
 //отображаем третий потенциометр
 potenz(400,100,6,65,191,ob3);

 

Конечный код программы:

import processing.serial.*; //импорт Serial библиотеки
Serial port; //Идентификация Serial порта с именем "port"

//Считываемые значения
float ob1 = 0; //первый потенциометр
float ob2 = 0; //второй потенциометр
float ob3 = 0; //третий потенциометр

float color_R = 0; //красный цвет
float color_G = 0; //зеленый цвет
float color_B = 0; //синий цвет

void setup()
{
 size (500,200);
 port = new Serial(this, "COM3",9600);
 port.bufferUntil('\n');
}

void draw()
{
 background(color_R,color_G,color_B); //делаем заливку как сумма всех цветов
 
 //-------------------------------/ 
 /*--- Создание потенциометров --*/
 //-------------------------------/ 

 //отображаем первый потенциометр
 potenz(100,100,255,10,39,ob1);
 
 //отображаем второй потенциометр
 potenz(250,100,6,191,41,ob2);
 
 //отображаем третий потенциометр
 potenz(400,100,6,65,191,ob3);
 
}

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 color_R = map(val,0,255,0,255);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 color_G = map(val,256,511,0,255);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 color_B = map(val,215,767,0,255);
 }
}

//---------------------------------//
/* Функция создания потенциометров */
//---------------------------------//
/* Параметры потенциометра: */
//---------------------------//
// x0 начальная координата потенциометра по x
// y0 начальная координата потенциометра по y
// r цвет потенциометра
// g цвет потенциометра
// b цвет потенциометра

void potenz (float x0, float y0, int r,int g, int b, float ug)
{ 
 //рисуем основной колпачок
 strokeWeight(1); //толщина линии
 stroke(0); //делаем контур колпачка черным
 fill(#434242);//заливаем фигуру
 ellipse(x0,y0,100,100); //рисуем окружность с центром в координатах x0 y0
 
 //создаем грань
 stroke(r,g,b); //делаем контур r,g,b цветом
 fill(#747070); //заливка фигуры
 ellipse(x0,y0,80,80); //рисуем окружность
 
 //рисуем центральную ось
 fill(r,g,b); //заливаем фигуру r,g,b цветом
 ellipse(x0,y0,30,30); //рисуем окружность
 
 //Вычисляем конечные координаты метки
 float x1 = x0 + (cos(ug)*50); //координата x1
 float y1 = y0 + (sin(ug)*50); //координата y1


 //создаем метку на потенциометре
 strokeWeight(10); //толщина линии
 stroke(r,g,b); //заливка линии
 line(x0,y0,x1,y1); //рисуем линию
 
}

 

Программа готова, теперь можно запускать. Результат будет такой:

 

Пример 3

Можно запрограммировать объект, который будет менять свое расположение на экране и свои размеры, используя потенциометры. Небольшой пример, для понимания самой идеи. 

Код программы:

import processing.serial.*; //импорт Serial библиотеки
Serial port; //Идентификация Serial порта с именем "port"

/* Параметры кубика */
float x0 = 0; //координата кубика по x
float y0 = 0; //координата кубика по y
float d = 10; //длина кубика
float h = 10; //высота кубика


/* Считываемые значения */
float ob1 = 0; //первый потенциометр
float ob2 = 0; //второй потенциометр
float ob3 = 0; //третий потенциометр

void setup()
{
 size (500,500);
 port = new Serial(this, "COM3",9600);
 port.bufferUntil('\n');
}

void draw()
{ 
 background(#D38446); //заливка экрана
 x0 = x0 + ob1; //изменение координаты кубика по x на значение получаемое с потенциометра 1
 y0 = y0 + ob2; //изменение координаты кубика по y на значение получаемое с потенциометра 2
 d = d + ob3; //изменение длины кубика на значение получаемое с потенциометра 3
 h = h + ob3; //изменение высоты кубика на значение получаемое с потенциометра 3
 
 
 if (x0 >= width)
 {
 x0 = 0; 
 }
 else if (y0 >= height)
 {
 y0 = 0;
 }
 
 rect(x0,y0,d,h); //отображаем кубик

}

void serialEvent (Serial port)
{
 
 float val = float (port.readStringUntil ('\n'));
 
 if ( val >= 0 && val <=255)
 {
 ob1 = map(val,0,255,0,5);
 }
 else if (val >= 256 && val <=511)
 {
 ob2 = map(val,256,511,0,5);
 }
 else if (val >= 512 && val <=767)
 {
 ob3 = map(val,512,767,0,5);
 }
}

Идея такова: у нас имеется кубик. В самом начале он находится в верхнем левом углу, и имеет размеры 10x10 px. После мы снимаем значения с потенциометров, Если мы изменяем сопротивление первого или второго резистора, наш кубик изменяет свои координаты по x и y координате соответственно. Чем сильнее поворачиваем резистор, тем быстрее происходит изменение координат. 

 x0 = x0 + ob1; //изменение координаты кубика по x на значение получаемое с потенциометра 1
 y0 = y0 + ob2; //изменение координаты кубика по y на значение получаемое с потенциометра 2

 

Если мы изменяем сопротивление третьего резистора, то мы изменяем размеры кубика по ширине и высоте. 

 d = d + ob3; //изменение длины кубика на значение получаемое с потенциометра 3
 h = h + ob3; //изменение высоты кубика на значение получаемое с потенциометра 3

Т.к, кубик может выйти за пределы окна, и мы перестанем его видеть, в программе организован возврат кубика в нулевые координаты:

 if (x0 >= width)
 {
 x0 = 0; 
 }
 else if (y0 >= height)
 {
 y0 = 0;
 }

width и height  - это зарезервированные переменные (их не надо объявлять), которые обозначают ширину и высоту окна. 

Если координата x (y) кубика вышла за пределы окна, то мы координате x (y) присваиваем значение 0, и возвращаем кубик в начальное положение.

Видео работы такой программы:

Задание для самостоятельного выполнения

  1. К примеру 2. Сделайте так, чтобы вызывая функцию potenz(), можно было бы также задавать размер потенциометров. Т.е нужно добавить в аргумент функции еще одну переменную и изменить немного саму функцию;
     
  2. К примеру 3. Переписать программу так, чтобы увеличивая сопротивление первого резистора - кубик перемещался вправо, а при уменьшении сопротивления - влево. Увеличивая сопротивление второго - перемещался вниз, при уменьшении - вверх. Изменяя же третий потенциометр, с увеличением сопротивления размер кубика увеличивался, а с уменьшением - уменьшался. 

    Т.е надо найти середину потенциометров. И задать в программе диапазон, в котором будет равновесие. Например сопротивление меняется от 0 до 22. Таким образом, диапазон равновесия потенциометра будет от 10 до 12. Уменьшение будет от 0 до 10. Увеличение от 12 до 22. Если повернете в одну сторону - произойдет уменьшение сопротивления, в другую - увеличение.

Заключение

Это был третий урок в разделе "Arduino и Processing". Надеемся, что урок был для Вас полезен и понятен. Также надеемся, что выполнение заданий не вызовет трудностей, и Вы справитесь самостоятельно. Если же возникнут вопросы или проблемы, задавайте их либо в комментариях, либо на форуме

 


←Предыдущий урок  |  Следующий урок


 

Категория: Arduino и Processing | Добавил: GM (09.11.2015)
Просмотров: 4126 | Рейтинг: 5.0/6
Всего комментариев: 0
avatar