Я расскажу о том, как наладить связь любого микроконтроллера Arduino (для примера взята Arduino Uno) и своей программы на Processing. Добавив ко всему прочему знания о Java, на котором основывается Processing, можно будет дописать проект под управление всем компьютером, а не только собственным приложением. Тема управления компьютером программой на Java не есть чем то секретным, погуглите и все найдете, уверяю вас.
Скачиваем среды для разработки (IDE)
Существует много интегрированных сред разработки для программирования микроконтроллеров на чистом Си. Из них можно отметить самые удобные: Atollic, Eclipse, Keil.
Однако для простоты и доступности данного руководства я буду использовать редактор Arduino IDE и писать на Ардуино Си. Скачать такой редактор можно с официального сайта Arduino.
Среду разработки для программирования на Procrssing так же можно скачать с официального сайта.
Стоит отметить, приличия ради, что данные IDE очень похожи, потому что написаны на одном движке. И когда создавался Arduino основатели старались как можно больше упростить свой редактор кода, как это и было сделано в Processing редакторе.
Arduino. Собираем схему и пишем код
В данном примере я буду использовать Arduino Uno. К ней будет подключена кнопка, потенциометр и светодиод. Соответственно я могу выдавать логический 0 или 1. Читать логический 0 или 1. И проводить Аналого-цифровое преобразование(ADC или АЦП), получая числа от 0 до 1023 (в Arduino Uno 10-ми разрядный АЦП) в зависимости от положения потенциометра. Большего для примера и не нужно, так как это основные функции, которые может делать микроконтроллер.
Схема подключения:

На схеме светодиод анодом подключен к 5V через ограничивающий резистор ( минимум 220 Ом, желательно 500 Ом), катодом к пину D11. Кнопка замыкает землю и пин D2. Потенциометр меняет потенциал на пине A1.
Задача микроконтроллера следующая: Если по последовательному интерфейсу (Serial COM port) приходит сообщение «LED — H» — засветить светодиод. Если приходит сообщение «LED — L» — затушить светодиод. Каждые 250мс отправлять сообщение в последовательный порт (в данном случае на экран компьютера) сообщение «Pot — » и число, полученное аналоговым чтением пина A1. При нажатии кнопки единожды отсылать сообщение «Button is pressed!».
Вот мое предложение решения данной задачи (не пример для подражания):
#define pinPot A1
#define pinLed 11
#define pinBtn 2
void setup() {
pinMode(pinPot, INPUT);
pinMode(pinLed, OUTPUT);
pinMode(pinBtn, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("The program starts.\n\n");
}
void loop() {
/* INITIAL VARIABLES. Segment 1 */
static char potMes[] = "Pot - ";
static char btnMes[] = "Button is pressed!";
static char passLight[] = "Led - ";
static int passLength = sizeof(passLight) - 1;
static int sizepm = sizeof(potMes) - 1;
static int sizebtn = sizeof(btnMes) - 1;
static bool flagLedState = LOW;
static bool flagBtnPress = false;
static long int curTime = 0;
static const int period = 200;
static bool flagEnableRead = false;
/* INITIAL VARIABLES. Segment 1 */
/* FUNCTIONS CALL. Segment 2 */
/*
* Led is attached to HIGH voltage from one side
* And to pin on the other side
* By that the inverting logic
*/
ReadSerialForLed(passLight, passLength, &flagLedState);
digitalWrite(pinLed, !flagLedState);
/*
* Button pin always is pulled to the HIGH voltage
* And only when button is pressed - Voltage on pin goes to GROUND
* So it is need to invert logic when read pins
*/
if(!Bounce(pinBtn) && flagBtnPress == false){
for(int i = 0; i < sizebtn; i++){
Serial.write(btnMes[i]);
}
Serial.print("\n");
flagBtnPress = true;
if(!flagEnableRead){
curTime = millis();
flagEnableRead = true;
}
}else if(Bounce(pinBtn)){
flagBtnPress = false;
}
/*
* Read and send Info "Pot - " + var Only after first press on button
* Every 'period'ms
*/
if(millis() - curTime > period && flagEnableRead){
SendData(pinPot, potMes, sizepm);
curTime = millis();
}
/* FUNCTIONS CALL. Segment 2 */
}
/*
* Pot - pin with potentiometer
* pMes - Array with message before Pot value
* sp - size of potentiometer message
*/
void SendData(int Pot, char* pMes, int sp){
static int varP[2];
varP[0] = analogRead(Pot);
varP[1] = varP[0]/256; // 0 - 3 (256 - 1024)
varP[0] = varP[0]%256; // 0 - 255
//Send Message
for(int i = 0; i < sp; i++){
Serial.write(char(pMes[i]));
}
//Send 2 bits of data
//Serial.write(varP[0]);
//Serial.write(varP[1]);
Serial.print(analogRead(Pot));
Serial.print("\n");
}
/*
* Function, which is reads button pin with the bounce
*/
bool Bounce(int btn){
if(digitalRead(btn) == true){
delay(15);
if(digitalRead(btn) == true){
return true;
}else{
return false;
}
}else{
return false;
}
}
/*
* If Message from Serial port, which you read will be the same to passLight
* So look at the next symbol after Pass Message. If it is symbol 'H' - make LED to light
* If it is 'L' - make LED off.
*/
void ReadSerialForLed(char *passLight_f, int passLength_f, bool* flagLedState_f){
static char sym;
static int cntPass = 0;
static bool readyGetLed = LOW;
while (Serial.available() > 0) {
sym = Serial.read();
if(sym == passLight_f[cntPass] && !readyGetLed){
cntPass++;
}else if (!readyGetLed){
cntPass = 0;
}else if(readyGetLed){
if(sym == 'H'){
*flagLedState_f = HIGH;
}else if(sym == 'L'){
*flagLedState_f = LOW;
}
}
if(cntPass == passLength_f){
readyGetLed = HIGH;
}
}
}
Комментарий: Светодиод подключен анодом к питаю. Это инвертирует логику состояния светодиода и больше никакой пользы не приносит. Кнопка не обвязана подтягивающим резистором из соображений экономии, так как в Arduino Uno имеются встроенные подтягивающие резисторы, которые включаются в схему при инициализации пина в режим INPUT_PULLUP.
Так же в прошивке сообщения о значении снятого с потенциометра отсылаются только после первого нажатия на кнопку!
Что бы залить прошивку в плату не забывайте выбрать порт и плату.

Если вы не знаете какой COM порт у вас отведен для платы Arduino, то на Windows заходим в
Панель управления -> Диспетчер устройств и нажимаем на вкладку «Порты COM»

Если у вас COM порт не подписан как у меня — всегда можно отсоединить Arduino и посмотреть который порт пропадет. А вот если никакой не пропал и Ардуина вовсе не распознается компьютером — значит пора поискать решение в интернете. Но начните с обновления драйверов или смены платы.
Когда все получится — попробуйте открыть монитор порта и ввести «Led — H», «Led — L», по нажимайте на кнопку, покрутите потенциометр и смотрите на экран, все ли правильно выводится.
Наигрались — поменяйте слегка код.
Замените последнюю строку кодом из комментария.
//Send 2 bits of data //Serial.write(varP[0]); //Serial.write(varP[1]); Serial.print(analogRead(Pot));
Теперь значения с потенциометра не будут выглядеть читабельными, но такой маневр требуется для программы на Processing.
Processing. Пишем программу, которая взаимодействует с микроконтроллером
Суть связи программы на Processing и микроконтроллера очень проста. Для этого языка программирования существует библиотека Serial, которая позволяет принимать сообщения, отправленные как
Serial.write();, а так же позволяет отправлять сообщения как Serial.print();. Важно отметить, что при подобной отправке сообщения оно будет записано в буфер порта, а значит будет прочитано микроконтроллером. Так что нам осталось только подключиться к нужному Serial порту и принимать/отправлять на него сообщения.Следующая программа подключит библиотеку Serial и напишет в консоли редактора список всех COM портов, к которым можно подключиться.
import processing.serial.*; void setup() { String[] port = Serial.list(); for(int i = 0; i < port.length; i++){ print("Port number #" + i + " "); println(Serial.list()[0]); } } void draw() {}
Когда вы напишете код в редактор и нажмете на кнопку «Пуск» (стрелочка 1 на картинке), то появится окно приложения(2) и в консоли(3) выведется список COM портов.

У меня только один такой COM порт и в листе, как в массиве, он будет находиться под номером 0. Из этих соображений объекту класса Serial:
Serial port; при его создании будет указан именно первый элемент списка портов port = new Serial(this, Serial.list()[0], 9600);Залейте в Ардуину нашу последнюю прошивку с изменением. После чего напишите вот эту программу и запустите ее. В ней Каждые 500 миллисекунд отправляется сообщение в COM порт потушить или зажечь светодиод. И если все у вас сделано правильно, то после запуска приложения светодиод должен мигать.
import processing.serial.*; Serial port; // Create object from Serial class void setup(){ port = new Serial(this, Serial.list()[0], 9600); } void draw(){ delay(500); port.write("Led - H"); delay(500); port.write("Led - L"); }
Или вот другой пример. Светодиод будет менять свое состояние после любого нажатия на окно приложения (размеры которого 800х800px) кнопкой мыши.
import processing.serial.*; Serial port; // Create object from Serial class int cnt = 0; void setup(){ size(800, 800); port = new Serial(this, Serial.list()[0], 9600); } void draw(){} void mousePressed() { cnt++; if(cnt % 2 == 1){ port.write("Led - H"); }else{ port.write("Led - L"); } }
Processing. Пример многофункционального приложения
Данное элементарное приложение симулирует «полет в космосе», если это можно так назвать. Значение с потенциометра изменяет скорость полета, нажатие на кнопку меняет направление полета. А любое нажатие кнопки мыши на окно приложения — меняет состояние светодиода (да, ничего оригинальнее я не придумал).
Мой код далек от совершенства, не принимайте его как хороший пример. Это просто пример, который работает. Вот, собственно, он.
import processing.serial.*; Serial port; // Create object from Serial class int val; // Data received from the serial port (symbol) int pot; // Data from potentiometer String potMes = "Pot - "; String btnMes = "Button is pressed!"; int cntPM = 0; // Counter Potentiometer Message. // When it equals to length of Pot Mess - get value. int cntBM = 0; int cntBtnPress = 0; int cntMousePress = 0; Star[] stars = new Star[1000]; float speed; int dir = 1; void setup(){ size(800, 800); for(int i = 0; i < stars.length; i++){ stars[i] = new Star(); } frameRate(60); // 60 Frames per second port = new Serial(this, Serial.list()[0], 9600); // Wait for first message from Arduino delay(2000); while (port.available() > 0) { val = port.read(); print(char(val)); } } void draw(){ if (port.available() > 0) { val = port.read(); cntPM = CheckSymbol(potMes, cntPM, char(val), cntPM); cntBM = CheckSymbol(btnMes, cntBM, char(val), cntBM); } DrawRain(pot, 0, 1023); } void DrawRain(int speed_f, int min, int max){ background(0); translate(width/2,height/2); speed = dir*map(speed_f, min, max, 0, 50); for(int i = 0; i < stars.length; i++){ stars[i].go(); stars[i].update(); stars[i].show(); } } int CheckSymbol(String mes, int index, char sym, int ret_val){ if(mes.charAt(index) == sym && ret_val < (mes.length() - 1)){ return (ret_val + 1); }else if( ret_val == (mes.length() - 1) && mes.equals(potMes) ){ if(port.available() > 0){ pot = port.read(); // First 0-255 value } if(port.available() > 0){ pot += 256*port.read(); // Last 2 bits 256 - 1024 } }else if( ret_val == (mes.length() - 1) && mes.equals(btnMes) ){ cntBtnPress++; dir = -dir; } return 0; } void mousePressed() { cntMousePress++; if(cntMousePress % 2 == 1){ port.write("Led - H"); }else{ port.write("Led - L"); } }
Заключение
Думаю, нужно написать, что идею последней программы я подцепил у одного программиста — Daniel Shiffman, который снимает ролики, понятные даже детям, о программировании на Processing (решено более 140 визуальных задач).
Когда я пытался сам разобраться в том что и как нужно делать для связи Processing и Arduino мне очень помогли вот эти сайты:

