Это было неизбежно. Увлекшись электроникой, я должен был рано или поздно дойти и до программирования микроконтроллеров. А что может быть проще программирования 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 прямо на макетной плате

EnglishRussianUkrainian