openFrameworks и Arduino


openFrameworks
openFrameworks — это обёртка С++ классов вокруг нескольких библиотек, т.е. что-то вроде клея для соединения этих библиотек в единое целое:
* OpenGL — для отображения графики
* FreeImage — для манипуляций с изображениями
* Freetype — для отображения и работы со шрифтами
* rtAudio — для аудио
* Quicktime (mac или pc) — для проигрывания видео и видеозахвата

Ключевые концепции openFrameworks:
* упрощение вещей.
* вы можете использовать любую из его частей независимо от других.
* предоставление прямого доступа к данным; например, пиксели изображения.

OpenFrameworks предназначен для «творческого кодинга» («creative coding») и может работать под Windows, MacOS X и Linux.
тип лицензии: MIT

чтобы работать с OpenFrameworks нужно иметь установленный VS C++ 2008
(да, по сравнению с Processing, где всё идёт в комплекте — это минус).

Скачиваем zip-архив с OpenFrameworks.
Обратите внимание, что OpenFrameworks выкладывается в двух версиях.
Простой и «толстой» (FAT); в последней содержатся дополнительные примеры (например, работа с OpenCV).

of_preRelease_v0061_vs2008_FAT.zip

Полученый архив нужно просто распаковать в удобную директорию
Например:

c:\DevTools\of_preRelease_v0061_vs2008_FAT\

и всё. Можно смотреть примеры.
Переходим в

\apps\examples\

, выбираем понравившийся проект, открываем директорию и запускаем .vcproj
Запустится VS C++ и теперь остаётся только запустить сборку проекта.

Создание нового openFrameworks-проекта

В readme описан процесс создания нового OpenFrameworks-проекта
и состоит он в копировании тестового пустого проекта в проект с новым названием:

a) скопируйте директорию внутри apps и вставьте в ту же самую директорию
т.е. (скопируйте «emptyExample» и вставьте «копия emptyExample»)

b) переименуйте получившуюся директорию, а внутри директории, переименуйте файлы .vcproj и .sln
т.е. переименуйте:
emptyExample.vcproj
в
coolExample.vcproj

c) откройте файл .vcproj в текстовом редакторе. измените старое название проекта на новое:


например, если скопировали emptyExample и хотите назвать его "coolExample" ,то файл должен выглядеть так:


в solution-файле, измените название .vcproj -файла:

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emptyExample", "emptyExample.vcproj", "{7FD42DF7-442E-479A-BA76-D0022F99702A}"
	ProjectSection(ProjectDependencies) = postProject
		{5837595D-ACA9-485C-8E76-729040CE4B0B} = {5837595D-ACA9-485C-8E76-729040CE4B0B}
	EndProjectSection
EndProject

на название, которое вы дали своему файлу:

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "coolExample", "coolExample.vcproj", "{7FD42DF7-442E-479A-BA76-D0022F99702A}"
	ProjectSection(ProjectDependencies) = postProject
		{5837595D-ACA9-485C-8E76-729040CE4B0B} = {5837595D-ACA9-485C-8E76-729040CE4B0B}
	EndProjectSection
EndProject

Обратите внимание, что в OpenFrameworks уже реализвано общение с контроллером Arduino по протоколу Firmata.
см. пример

apps\examples\firmataExample\

работа осуществляется через класс ofArduino (который, соответственно, использует класс ofSerial)
см.

libs\openFrameworks\communication\

пример OpenCV находится в дополнительных примерах, которые идут в расширенной ("толстой") версии OpenFrameworks:

apps\addonsExamples\opencvExample\

поддержка OpenCV реализована в виде аддона:

addons\ofxOpenCv\

и по версии используемых lib-файлов

addons\ofxOpenCv\libs\opencv\lib\vs2008\

, используется версия OpenCV 1.1

openFrameworks и Arduino

Рассмотрим работу примера общения с Arduino в примере firmataExample, заодно поймём как нужно работать в OpenFrameworks.

Запустим проект

apps\examples\firmataExample\

Он состоит из трёх файлов:
main.cpp
testApp.h
testApp.cpp

содержимое main.cpp:

#include "ofMain.h"
#include "testApp.h"
#include "ofAppGlutWindow.h"

//========================================================================
int main( ){

    ofAppGlutWindow window;
	ofSetupOpenGL(&window, 800,600, OF_WINDOW);			// <-------- setup the GL context

	// this kicks off the running of my app
	// can be OF_WINDOW or OF_FULLSCREEN
	// pass in width and height too:
	ofRunApp( new testApp());

}

Кажется, тут всё понятно: создаётся окно, устанавливаются его параметры, а затем создаётся новый объект нашего testApp, который передаётся параметром в ofRunApp(), которая делает всю остальную работу, вызывая установочные testApp->setup() и testApp->update(), а затем в цикле отрисовки вызывая testApp->draw()

содержимое testApp.h:

#ifndef _TEST_APP
#define _TEST_APP

#include "ofMain.h"

class testApp : public ofSimpleApp{

public:

	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void keyReleased(int key);

	void mouseMoved(int x, int y );
	void mouseDragged(int x, int y, int button);
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void windowResized(int w, int h);

	void setupArduino();
	void updateArduino();

	ofImage				bgImage;
	ofTrueTypeFont		font;
	ofArduino	ard;
	bool		bSetupArduino;			// flag variable for setting up arduino once

};
#endif

видим класс testApp ,у которого объявлено несколько функций.
самые важные - это:

void setup();
void update();
void draw();

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

видим объект для общения с Arduino по протоколу Firmata:

ofArduino	ard;

Соответственно в testApp.cpp написана реализация функций класса:

setup() ,как ясно из названия - это аналог setup() в Wiring-e и Processing-е; функция в которой происходит начальная установка и инициализация параметров.
В данном примере - установка фона, подгрузка картинки, загрузка шрифта и подключение к контроллеру Arduino:

#include "testApp.h"

void testApp::setup(){

	ofSetVerticalSync(true);
	ofSetFrameRate(60);

	ofBackground(255,0,130);

	bgImage.loadImage("firmata.png");
	font.loadFont("franklinGothic.otf", 20);

	// the connection speed has been changing in the
	// arduino firmata sketch. in 0017 it's 57600
	// if you have problems try commenting/uncommenting
	// to change the speed

	// ard.connect("/dev/ttyUSB0", 115200);
	ard.connect("/dev/ttyUSB0", 57600);

	bSetupArduino	= false;							// flag so we setup arduino when its ready, you don't need to touch this :)
}

Нужно отметить, что для подключения к COM-порту под Windows придётся писать строчку вида:

ard.connect("\\\\.\\COM19", 57600);
void testApp::update(){

	if ( ard.isArduinoReady()){

		// 1st: setup the arduino if haven't already:
		if (bSetupArduino == false){
			setupArduino();
			bSetupArduino = true;	// only do this once
		}
		// 2nd do the update of the arduino
		updateArduino();
	}

}

//--------------------------------------------------------------
void testApp::setupArduino(){

	// this is where you setup all the pins and pin modes, etc
	for (int i = 0; i < 13; i++){
		ard.sendDigitalPinMode(i, ARD_OUTPUT);
	}

	ard.sendDigitalPinMode(13, ARD_OUTPUT);
	ard.sendAnalogPinReporting(0, ARD_ANALOG);	// AB: report data
	ard.sendDigitalPinMode(11, ARD_PWM);		// on diecimelia: 11 pwm?*/
}

//--------------------------------------------------------------
void testApp::updateArduino(){

	// update the arduino, get any data or messages:
	ard.update();
	ard.sendPwm(11, (int)(128 + 128 * sin(ofGetElapsedTimef())));   // pwm...

}

Нетрудно догадаться, что функция draw() - аналог одноимённой функции Processing-а:

void testApp::draw(){
	bgImage.draw(0,0);

	if (!ard.isArduinoReady()){
		font.drawString("arduino not ready\n", 545, 40);
	} else {
		font.drawString("analog pin 0: " + ofToString(ard.getAnalog(0)) +
						"\nsending pwm: " + ofToString((int)(128 + 128 * sin(ofGetElapsedTimef()))), 545, 40);

	}
}

так же видим, что при нажатии/отпускании кнопки мышки контроллеру Ardunio будет послано сообщение зажечь/погасить сигнальный светодиод на 13 порту.

void testApp::mousePressed(int x, int y, int button){
	ard.sendDigital(13, ARD_HIGH);
}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
	ard.sendDigital(13, ARD_LOW);
}

После сборки проекта в каталоге

apps\examples\firmataExample\bin\

появится исполняемый файл
firmataExample.exe
,а так же будут скопированы необходимые для работы и распространения dll-ки:
fmodex.dll
fmodexL.dll
FreeImage.dll
FreeType-6.dll
glut32.dll
Zlib.dll

так же обратите на подкаталог data, в котором содержатся необходимые для работы файлы:
firmata.png - файл изображения Arduino
franklinGothic.otf - файл используемого шрифта

Cвой openFrameworks-проект для Arduino

Итак, вроде всё более-менее понятно.
Чтобы закрепить материал нужно попробовать написать свой проект 🙂
Т.к. проект будет для общения с Arduino - я скопирую не пустой проект emptyExample, а уже рассмотренный firmataExample.

Назову проект myArduinoExample
Проект будет аналогом рассмотренного ранее проекта Processing-а arduio_output.
Т.е. выведем в окошко квадратики состояния цифровых выходов ардуины; каждый квадратик будет обозначать один цифровой выход контроллера от 13 до 0. Щелкая по нему, мы переключаем состояние межу HIGH и LOW.
Однако просто выводить квадратики скучно, поэтому возьмём фотографию контроллера Arduino и будем щёлкать по изображению портов.

переименую и отредактирую файлы
myArduinoExample.sln
и
myArduinoExample.vcproj
заменив строчку firmataExample на myArduinoExample.
Заменим и переименуем картинку.
Пора редактировать код 🙂

Первым делом, нам нужно определить положение портов на картинке:
для этого добавим в класс testApp две переменные mouseX и mouseY для сохранения координат нажатия мышки.
Установку значения этих переменных будем производить по нажатию мышки в обработчике mousePressed():

void testApp::mousePressed(int x, int y, int button){
	printf("[click] x: %d y: %d\n", x, y);
	mouseX=x;
	mouseY=y;
	}

вывод координат добавим в функцию draw():

// выводим где был последний клик мышкой :)
	if(mouseX || mouseY){
		ofSetColor(50, 100, 200);
		font.drawString("x: " + ofToString(mouseX) + " y: " + ofToString(mouseY), 600, 100);
		ofSetColor(255, 255, 255);
	}

Обратите внимание - шрифт изначально выводится белым цветом 🙂
по примерам видно, что для того чтобы изменить цвет нужно добавить строчку

ofSetColor(50, 100, 200);
font.drawString("x: " + ofToString(mouseX) + " y: " + ofToString(mouseY), 600, 100);

в конце нужно добавить

ofSetColor(255, 255, 255);

, чтобы цвет не залил весь экран 😉

Собираем код, щёлкаем по картинке и получаем координаты:

по y - около 95
а по X:
13 - 300
12 - 320
11 - 340
10 - 360
9 - 380
8 - 400
7 - 440
6 - 460
5 - 480
4 - 500
3 - 520
2 - 540

Остаётся нарисовать в этих местах квадратики.
Как задаётся цвет мы уже знаем, квадратик же рисуется функцией

ofRect(x, y, w, h);

Вот и всё. Остаётся определить место клика мышкой и просто менять состояние порта на противоположное.

Для хранения текущего состояния порта заведём массив

int pins[PIN_COUNT];

Обнуляем этот массив в функции setup(), а затем добавляем проверочный код на какой квадратик порта кликнули в функцию-обработчик mousePressed():

void testApp::mousePressed(int x, int y, int button){
	printf("[click] x: %d y: %d\n", x, y);
	mouseX=x;
	mouseY=y;

	// проверка попадания по Y
	if(mouseY>90 && mouseY<105){
		printf("[i] Y ok!\n");

		// проверка попадания по X
		if(mouseX>290 && mouseX<560){
			// определяем куда кликнули :)
			for(int i=2; i<=13; i++){
				float xx;
				if(i<=7){
					xx = 540 - 20*(i-1) + 10;
				}
				else{
					xx = 400 - 20*(i-7) + 10;
				}
				if( abs(xx+7.5-x)<13 ){

					printf("[i] X ok! pin: %d\n", i);

					// меняем состояние порта
					if(pins[i]){
						ard.sendDigital(i, ARD_LOW);
						pins[i]=0;
					}
					else{
						ard.sendDigital(i, ARD_HIGH);
						pins[i]=1;
					}
					break;
				}
			}
		}
	}
}

и соответственно меняем цвет квадратика в функции draw():

void testApp::draw(){
	// показываем изображения (в качестве фона)
	bgImage.draw(0,0);

	if (!ard.isArduinoReady()){
		font.drawString("arduino not ready\n", 545, 40);
	} 
	
	// выводим где был последний клик мышкой :)
	if(mouseX || mouseY){
		ofSetColor(50, 100, 200);
		font.drawString("x: " + ofToString(mouseX) + " y: " + ofToString(mouseY), 600, 100);
		ofSetColor(255, 255, 255);
		
		//ofDrawBitmapString("x: " + ofToString(mouseX) + " y: " + ofToString(mouseY), 545, 130);
	}

	float x,y, w=15, h=15;
	// нарисуем квадратики портов
	for(int i=2; i<=13; i++){
		// в зависимости от состояния порта - определяем цвет
		if(pins[i]){
			ofSetColor(200, 0, 0);
		}
		else{
			ofSetColor(0, 200, 0);
		}
		y = 90;
		if(i<=7){
			x = 540 - 20*(i-1) + 10;
		}
		else{
			x = 400 - 20*(i-7) + 10;
		}
		// рисуем квадратик
		ofRect(x, y, w, h);
	}
	ofSetColor(255, 255, 255);
}

Вот как выглядит окошко программы:

Скачать программу с исходниками. (2.5 Mb - так много потому что в комплекте идут dll-библиотеки используемые openFrameworks-ом, без которых программа не запустится )

Вывод - действительно просто, быстро и удобно 🙂 А ещё OpenGL 😉

читать ещё: openFrameworks vs Processing

Ссылки
официальный сайт - http://www.openframeworks.cc
FAQ - http://www.openframeworks.cc/about/faq
форум - http://www.openframeworks.cc/forum/index.php
wiki - http://wiki.openframeworks.cc

http://en.wikipedia.org/wiki/OpenFrameworks
http://wiki.openframeworks.cc/index.php?title=OfAmsterdam
OpenGL tutorials

По теме
Практическое программирование Arduino- программирование работы с COM-портом
Arduino и Matlab
Arduino и LabVIEW
Processing и Arduino


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна конкурс манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение