Всем привет! В интернете бытует заблуждение, что для управления компьютером при помощи самодельной электроники нужны только специальные платы, которые могут распознаваться как USB HID устройства. А касаемо Arduino все только и говорят о Arduino Leanardo. Такие популярные библиотеки как Keyboard и Mouse, которые позволяют создавать эмуляцию работы мыши или клавиатуры посредством микроконтроллера предназначены только для пары плат Arduino, Leonardo в их числе.
Я расскажу о том, как наладить связь любого микроконтроллера Arduino (для примера взята Arduino Uno) и своей программы на Processing. Добавив ко всему прочему знания о Java, на котором основывается Processing, можно будет дописать проект под управление всем компьютером, а не только собственным приложением. Тема управления компьютером программой на Java не есть чем то секретным, погуглите и все найдете, уверяю вас.
Существует много интегрированных сред разработки для программирования микроконтроллеров на чистом Си. Из них можно отметить самые удобные: Atollic, Eclipse, Keil.
Однако для простоты и доступности данного руководства я буду использовать редактор Arduino IDE и писать на Ардуино Си. Скачать такой редактор можно с официального сайта Arduino.
Среду разработки для программирования на Procrssing так же можно скачать с официального сайта.
Стоит отметить, приличия ради, что данные IDE очень похожи, потому что написаны на одном движке. И когда создавался Arduino основатели старались как можно больше упростить свой редактор кода, как это и было сделано в Processing редакторе.
В данном примере я буду использовать 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!».
Вот мое предложение решения данной задачи (не пример для подражания):
Комментарий: Светодиод подключен анодом к питаю. Это инвертирует логику состояния светодиода и больше никакой пользы не приносит. Кнопка не обвязана подтягивающим резистором из соображений экономии, так как в Arduino Uno имеются встроенные подтягивающие резисторы, которые включаются в схему при инициализации пина в режим INPUT_PULLUP.
Так же в прошивке сообщения о значении снятого с потенциометра отсылаются только после первого нажатия на кнопку!
Что бы залить прошивку в плату не забывайте выбрать порт и плату.
Если вы не знаете какой COM порт у вас отведен для платы Arduino, то на Windows заходим в
Панель управления -> Диспетчер устройств и нажимаем на вкладку «Порты COM»
Если у вас COM порт не подписан как у меня — всегда можно отсоединить Arduino и посмотреть который порт пропадет. А вот если никакой не пропал и Ардуина вовсе не распознается компьютером — значит пора поискать решение в интернете. Но начните с обновления драйверов или смены платы.
Когда все получится — попробуйте открыть монитор порта и ввести «Led — H», «Led — L», по нажимайте на кнопку, покрутите потенциометр и смотрите на экран, все ли правильно выводится.
Наигрались — поменяйте слегка код.
Замените последнюю строку кодом из комментария.
Теперь значения с потенциометра не будут выглядеть читабельными, но такой маневр требуется для программы на Processing.
Суть связи программы на Processing и микроконтроллера очень проста. Для этого языка программирования существует библиотека Serial, которая позволяет принимать сообщения, отправленные как
Следующая программа подключит библиотеку Serial и напишет в консоли редактора список всех COM портов, к которым можно подключиться.
Когда вы напишете код в редактор и нажмете на кнопку «Пуск» (стрелочка 1 на картинке), то появится окно приложения(2) и в консоли(3) выведется список COM портов.
У меня только один такой COM порт и в листе, как в массиве, он будет находиться под номером 0. Из этих соображений объекту класса Serial:
Залейте в Ардуину нашу последнюю прошивку с изменением. После чего напишите вот эту программу и запустите ее. В ней Каждые 500 миллисекунд отправляется сообщение в COM порт потушить или зажечь светодиод. И если все у вас сделано правильно, то после запуска приложения светодиод должен мигать.
Или вот другой пример. Светодиод будет менять свое состояние после любого нажатия на окно приложения (размеры которого 800х800px) кнопкой мыши.
Данное элементарное приложение симулирует «полет в космосе», если это можно так назвать. Значение с потенциометра изменяет скорость полета, нажатие на кнопку меняет направление полета. А любое нажатие кнопки мыши на окно приложения — меняет состояние светодиода (да, ничего оригинальнее я не придумал).
Мой код далек от совершенства, не принимайте его как хороший пример. Это просто пример, который работает. Вот, собственно, он.
Думаю, нужно написать, что идею последней программы я подцепил у одного программиста — Daniel Shiffman, который снимает ролики, понятные даже детям, о программировании на Processing (решено более 140 визуальных задач).
Когда я пытался сам разобраться в том что и как нужно делать для связи Processing и Arduino мне очень помогли вот эти сайты:
Я расскажу о том, как наладить связь любого микроконтроллера 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!».
Вот мое предложение решения данной задачи (не пример для подражания):
Прошивка для Arduino Uno
#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 мне очень помогли вот эти сайты: