← На главную

Мои первые страшные опыты с Arduino

Это было неизбежно. Увлекшись электроникой, я должен был рано или поздно дойти и до программирования микроконтроллеров. А что может быть проще программирования AVR-микроконтроллеров в устройствах Arduino? Не удивительно, что начать я решил именно с них. Что же из этого получилось – читайте далее.

Примечание: Описанные далее действия производились под Linux, на Arduino Uno и, соответственно, используемом в этом устройстве микроконтроллере ATmega328P. Однако все написанное далее справедливо и для других моделей Arduino, а также мало отличаться в случае, если вы используете другую ОС.

Настройка Arduino IDE

Подключаем Arduino к компьютеру через USB порт и определяем при помощи dmesg имя соответствующего устройства. У меня это ttyACM0. Если увидели что-то вроде:

usb 1-1: new full-speed USB device number 59 using xhci_hcd usb 1-1: Device not responding to setup address. usb 1-1: device not accepting address 59, error -71 usb usb1-port1: unable to enumerate USB device

… значит проблема в USB-кабеле, используйте другой.

Смотрим, какие права стоят на это устройство:

ls -la /dev/ttyACM0

В разных дистрибутивах результат немного отличается. В Arch Linux я увидел:

crw-rw---- 1 root uucp 166, 0 Jan 1 21:34 /dev/ttyACM0

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

sudo usermod -a -G uucp eax

В Ubuntu название группы будет другим, а именно «dialout». После добавления пользователя в группу скорее всего придется перелогиниться. В выводе id должны видеть, что ваш пользователь теперь состоит в группе.

Качаем Arduino IDE отсюда и распаковываем куда-нибудь:

cd ~/temp tar --xz -xvf arduino-1.8.0-linux64.tar.xz mkdir ~/opt mv arduino-1.8.0 ~/opt/arduino

В каталоге с IDE можно запустить install.sh, чтобы он создал красивые икноки для вашего окружения рабочего стола. Я пользуюсь i3, поэтому никаких красивых иконочек мне не нужно.

После запуска IDE в Tools → Port выбираем /dev/ttyACM0. Затем жмем Files → Examples → Basics → Blink. Откроется следующий код:

void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000); }

Используемый в Arduino язык программирования, как не сложно заметить, является диалектом C/C++. Эта программа просто мигает встроенным в Arduino светодиодом. Вы тут все программисты, поэтому разжевывать, как именно работает код, полагаю, не требуется. Жмем Upload (Ctr+U) и… я лично получил такую ошибку:

libtinfo.so.5: cannot open shared object file: No such file or directory

Лечится так:

sudo pacman -S ncurses sudo ln -s /usr/lib/libncursesw.so /usr/lib/libtinfo.so.5

Если все было сделано правильно, теперь после нажатия Ctr+U Arduino начнет мигать желтым светодиодом с подписью L. Бывает так, что программа Blink уже была залита в Arduino заранее. В этом случае попробуйте изменить в коде частоту мигания и перезалить программу.

Работа без IDE

Arduino IDE довольно убога, как текстовый редактор, плюс я заметил в ней неприятные глюки при сохранении файлов. Мне, знаете ли, нравится писать код в Vim. Вам может нравиться Sublime Text или другой текстовый редактор. Так давайте же разберемся, как программировать Arduino в текстовом редакторе или IDE по вкусу.

Говорим и заодно сразу прописываем в ~/.bashrc:

export ARDUINODIR=/home/eax/opt/arduino

Далее нам понадобится файл arduino.mk, который доступен здесь. Однако не спешите его качать. Мне этот файл пришлось во многих местах подправить, чтобы он заработал с последней Arduino IDE. Детали скучны, поэтому я не буду вас ими грузить. Просто берите пропатченную мною версию. Если вам вдруг интересно, что именно я поменял, измененные строчки помечены вот так:

# patched by afiskon

Создаем файл Makefile такого содержания:

TARGET := main SOURCES := main.cpp BOARD := uno include arduino.mk

… а также main.cpp:

#include <Arduino.h> /* Номера пинов, к которым подключены аноды RGB-светодиода */ const int RLED = 9; const int GLED = 11; const int BLED = 10; /* Номера пинов, к которым подключены кнопки */ const int RBUTTON = 2; const int GBUTTON = 4; const int BBUTTON = 6; /* Номер аналогового входа, подключенного к фоторезистору */ const int LIGHT = 0; /* Как часто считывать состояния кнопок, в мс */ const int DELAY = 10; /* Как часто выводить уровень освещенности, в мс */ const int LIGHT_REPORT_PERIOD = 1000; /* Текущая яркость RGB-светодиода */ int rval = 120; int gval = 0; int bval = 250; /* * Была ли отпущена соответствующая кнопка. * * Эти переменные нужны, чтобы одно нажатие кнопки * не считалось за несколько. */ bool rbutton_released = true; bool gbutton_released = true; bool bbutton_released = true; /* Как долго не выводили уровень освещенности, в мс */ int silent_time = 0; void setup() { pinMode(RLED, OUTPUT); pinMode(GLED, OUTPUT); pinMode(BLED, OUTPUT); pinMode(RBUTTON, INPUT); pinMode(GBUTTON, INPUT); pinMode(BBUTTON, INPUT); Serial.begin(9600); } void loop() { /* Обработка нажатия первой кнопки */ if(digitalRead(RBUTTON) == HIGH) { if(rbutton_released) { rbutton_released = false; rval += 10; if(rval > 255) rval = 0; Serial.println("rval = " + String(rval)); } } else rbutton_released = true; /* То же самое для второй кнопки */ if(digitalRead(GBUTTON) == HIGH) { if(gbutton_released) { gbutton_released = false; gval += 10; if(gval > 255) gval = 0; Serial.println("gval = " + String(gval)); } } else gbutton_released = true; /* То же самое для третьей кнопки */ if(digitalRead(BBUTTON) == HIGH) { if(bbutton_released) { bbutton_released = false; bval += 10; if(bval > 255) bval = 0; Serial.println("bval = " + String(bval)); } } else bbutton_released = true; /* Задаем уровень ШИМ-сигнала на анодах RGB-светодиода */ analogWrite(RLED, rval); analogWrite(GLED, gval); analogWrite(BLED, bval); /* Небольшая задержка */ delay(DELAY); silent_time += DELAY; /* Выводим освещенность, если долго ее не выводили */ if(silent_time >= LIGHT_REPORT_PERIOD) { int light_val = analogRead(LIGHT); Serial.println("light_val = " + String(light_val)); silent_time = 0; } }

Как видите, программа стала сложнее. Этот код мы разберем подробнее чуть ниже. Если хотите, можете пока что оставить код программы Blink. Главное – не забудьте добавить в начале строчку:

#include <Arduino.h>

Для загрузки программы говорим make upload. Для просмотра сообщений от Arduino говорим make monitor. В последнем случае будет использован screen. Как альтернативный вариант, можно воспользоваться программой minicom:

sudo pacman -S minicom minicom -D /dev/ttyACM0 -b 9600

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

stty -F /dev/ttyACM0

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

Немного матчасти и разбор кода

Приведенная программа управляет RGB-светодиодом, а также считывает данные с трех кнопок и фоторезистора:

Мой первый проект на Arduino

RGB-светодиод – это по сути три светодиода в одном корпусе с общим катодом или анодом, которому соответствует наиболее длинная ножка из четырех. У меня был RGB-светодиод с общим катодом, то есть самую длинную ножку я подключал к минусу. Такой светодиод интересен тем, что его цвет со временем можно менять. Как видите, на фото светодиод светится фиолетово-розовым цветом. Если у вас нет RGB-светодиода, вы можете с тем же успехом использовать три отдельных светодиода.

Фоторезистор представляет собой резистор, чье сопротивление меняется в зависимости от того, как много света на него падает. Тот, что есть у меня, имеет сопротивление около 400 Ом при ярком свете и более 2 МОм (максимум, который может померить мой мультиметр) в полной темноте. Используя его в качестве одного из резисторов в делителе напряжения, можно оценить яркость света вокруг. Если у вас нет фоторезистора, можете с тем же успехом подключить обычный потенциометр.

У Arduino Uno есть 14 цифровых пинов, 6 аналоговых входов, а также пины с 5 В напряжения, 3.3 В и землей. В программе Blink мы использовали 13-ый пин со встроенным светодиодом. Подавая на него высокое и низкое напряжение, можно заставить светодиод мигать. Также цифровые пины позволяют считывать напряжение. В приведенной выше программе пины 2, 4 и 6 используются для чтения состояния кнопок. Пока это все не сильно отличается от уже знакомых нам GPIO пинов в Raspberry Pi.

Часть цифровых пинов помечены тильдой (~). У меня таких пинов 6, с номерами 3, 5, 6, 9, 10 и 11. Это означает, что пин может подавать ШИМ-сигнал (Широтно-Импульсная Модуляция или PWM, Pulse-Width Modulation). В данном контексте ШИМ всего лишь означает, что на пин будет подаваться сигнал с заданным коэффициентом заполнения. В Arduino коэффициент заполнения задается от 0 до 255. Например, если указать 127, то половину времени будет подаваться высокий сигнал, а половину – низкий. Если же указать 0, то сигнал будет все время низким. Частота ШИМ-сигнала в Arduino разная на разных пинах и отличается от модели к модели. В Arduino Uno частота составляет примерно 980 Гц на пинах 5 и 6, и примерно 490 Гц на остальных ШИМ-пинах.

ШИМ позволяет аппроксимировать подачу напряжения между 0 и 5 В. В приведенной программе нажатия на кнопки увеличивают ШИМ-сигнал на соответствующих анодах RGB-светодиода. В результате тремя кнопками регулируется яркость и цвет светодиода.

Наконец, аналоговые входы предназначены для более точного считывания напряжения, чем просто «высокий сигнал» и «низкий сигнал». В приведенном примере аналоговый вход A0 подключен к делителю напряжения из фоторезистора и резистора сопротивлением 10 кОм. Вызов analogRead(LIGHT) возвращает значение около 800 при ярком свете, 125 при свете от настольной лампы, 0 в полной темноте и  около 960, если посветить на фоторезистор лазерной указкой. В целом диапазон возможных возвращаемых значений лежит от 0 до 1023. Плюс к этому, аналоговые входы можно использовать точно так же, как и цифровые пины (без ШИМ), например:

pinMode(A1, OUTPUT); digitalWrite(A1, HIGH);

Fun fact! Фактически, теперь вы знаете, как сделать лазерную сигнализацию. Также с помощью светодиодов или ИК-светодиодов и фоторезистора можно сделать датчик движения (бесконтактный выключатель?) вроде того, что используется в сушилках для рук. Также помимо фоторезистора можно использовать фототранзистор, представляющий собой обычный транзистор, ток базы которого определяется интенсивностью света. Если желаете, можете реализовать эти проекты в качестве домашнего задания.

Заключение

С Arduino мне понравилось работать намного больше, чем с Raspberry Pi. У последней, насколько мне известно, аналоговых входов нет совсем, да и с ШИМ все как-то не слишком удобно. Плюс Arduino Uno – это всего лишь микроконтроллер ATmega328P, на котором нет этой бесполезной операционной системы и который при желании можно извлечь и впаять прямо в изготовленную по ЛУТ плату. В общем, для занятия электроникой это устройство куда интереснее.

Исходники к этой заметке вы найдете на GitHub. Как обычно, буду рад вашим вопросам и дополнениям.

Дополнение: Как собрать Arduino прямо на макетной плате