Мои первые страшные опыты с Arduino
18 января 2017
Это было неизбежно. Увлекшись электроникой, я должен был рано или поздно дойти и до программирования микроконтроллеров. А что может быть проще программирования AVR-микроконтроллеров в устройствах Arduino? Не удивительно, что начать я решил именно с них. Что же из этого получилось — читайте далее.
Примечание: Описанные далее действия производились под Linux, на Arduino Uno и, соответственно, используемом в этом устройстве микроконтроллере ATmega328P. Однако все написанное далее справедливо и для других моделей Arduino, а также мало отличаться в случае, если вы используете другую ОС.
Настройка Arduino IDE
Подключаем Arduino к компьютеру через USB порт и определяем при помощи dmesg
имя соответствующего устройства. У меня это ttyACM0. Если увидели что-то вроде:
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-кабеле, используйте другой.
Смотрим, какие права стоят на это устройство:
В разных дистрибутивах результат немного отличается. В Arch Linux я увидел:
То есть, для доступа к устройству пользователя нужно добавить в группу uucp:
В Ubuntu название группы будет другим, а именно «dialout». После добавления пользователя в группу скорее всего придется перелогиниться. В выводе id
должны видеть, что ваш пользователь теперь состоит в группе.
Качаем Arduino IDE отсюда и распаковываем куда-нибудь:
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. Откроется следующий код:
{
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) и… я лично получил такую ошибку:
file or directory
Лечится так:
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:
Далее нам понадобится файл arduino.mk, который доступен здесь. Однако не спешите его качать. Мне этот файл пришлось во многих местах подправить, чтобы он заработал с последней Arduino IDE. Детали скучны, поэтому я не буду вас ими грузить. Просто берите пропатченную мною версию. Если вам вдруг интересно, что именно я поменял, измененные строчки помечены вот так:
Создаем файл Makefile такого содержания:
SOURCES := main.cpp
BOARD := uno
include arduino.mk
… а также main.cpp:
/* Номера пинов, к которым подключены аноды 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. Главное — не забудьте добавить в начале строчку:
Для загрузки программы говорим make upload
. Для просмотра сообщений от Arduino говорим make monitor
. В последнем случае будет использован screen. Как альтернативный вариант, можно воспользоваться программой minicom:
minicom -D /dev/ttyACM0 -b 9600
Если подзабыли количество бод, указанное в программе, можно сказать:
Теперь, когда с настройкой рабочего окружения покончено, давайте разберемся, как работает приведенная программа.
Немного матчасти и разбор кода
Приведенная программа управляет RGB-светодиодом, а также считывает данные с трех кнопок и фоторезистора:
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. Плюс к этому, аналоговые входы можно использовать точно так же, как и цифровые пины (без ШИМ), например:
digitalWrite(A1, HIGH);
Fun fact! Фактически, теперь вы знаете, как сделать лазерную сигнализацию. Также с помощью светодиодов или ИК-светодиодов и фоторезистора можно сделать датчик движения (бесконтактный выключатель?) вроде того, что используется в сушилках для рук. Также помимо фоторезистора можно использовать фототранзистор, представляющий собой обычный транзистор, ток базы которого определяется интенсивностью света. Если желаете, можете реализовать эти проекты в качестве домашнего задания.
Заключение
С Arduino мне понравилось работать намного больше, чем с Raspberry Pi. У последней, насколько мне известно, аналоговых входов нет совсем, да и с ШИМ все как-то не слишком удобно. Плюс Arduino Uno — это всего лишь микроконтроллер ATmega328P, на котором нет этой бесполезной операционной системы и который при желании можно извлечь и впаять прямо в изготовленную по ЛУТ плату. В общем, для занятия электроникой это устройство куда интереснее.
Исходники к этой заметке вы найдете на GitHub. Как обычно, буду рад вашим вопросам и дополнениям.
Дополнение: Как собрать Arduino прямо на макетной плате
Метки: AVR, Электроника.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.