Изучаем Arduino: инструметы и методы технического волшебства

Блум Джереми

Часть IV. Дополнительные темы и проекты

 

В этой части

Глава 12. Аппаратные прерывания и прерывания по таймеру

Глава 13. Обмен данными с картами памяти SD

Глава 14. Подключение Arduino к Интернету

 

Глава 12. Аппаратные прерывания и прерывания по таймеру

Список деталей

Для повторения примеров главы вам понадобятся следующие детали:

• плата Arduino (рекомендуется Uno );

• USB-кабель для программирования платы Arduino;

• кнопка;

• пьезозуммер;

• RGB-светодиод с общим катодом;

• 1 резистор номиналом 10 кОм;

• 1 резистор номиналом 100 Ом;

• 1 резистор номиналом 150 Ом;

• 3 резистора номиналом 220 Ом;

• электролитический конденсатор 10 мкФ;

• микросхема 74НС14 (шесть инверторов с триггерами Шмитта);

• набор перемычек;

• 2 макетные платы.

Электронные ресурсы к главе

На странице http://www.exploringarduino.com/content/ch12 можно загрузить программный код, видеоуроки и другие материалы для данной главы. Кроме того, листинги примеров можно скачать со страницы www.wiley.com/go/exploringarduino в разделе Downloads.

Что вы узнаете в этой главе

Все предыдущие программы работали в синхронном режиме. В связи с этим возникали проблемы, например, выполнение команды delay() останавливает программу на некоторое время и не дает возможности Arduino осуществлять другие действия.

В главе 1 мы создали программный таймер, использующий функцию millis(), что

- 254 -

позволило избежать временного блокирования платы Arduino функцией delay().

Продолжим эту тему, добавив два таймера и аппаратные прерывания. Прерывания позволяют выполнять программу асинхронно, при наступлении определенного события (истечение временного интервала, изменение состояния входов и т. д.). Прерывания, как и следует из их названия, дают возможность остановить ход текущей программы Arduino, выполнить код прерывания, а затем вернуться к прерванной задаче. Далее мы узнаем, как осуществить прерывания по времени и при изменении состояния контактов. На основе этих знаний мы построим систему аппаратных прерываний и напишем программу, использующую прерывания таймера.

ПРИМЕЧАНИЕ

Видеоурок по прерываниям и аппаратному устранению дребезга можно посмотреть на странице

http://www.jeremyblum.com/2011103/07/arduino-tutorial-10-interrupts-and1hardware-debouncing. Найти данный видеофайл можно и на странице издательства Wiley.

 

12.1. Использование аппаратных прерываний

Аппаратные прерывания происходят при наступлении (или изменении) заданного состояния на входах-выходах. Аппаратное прерывание полезно, например, когда нужно изменить значение переменной, не проверяя непрерывно состояние кнопки.

Ранее мы устраняли дребезг путем опроса состояния кнопки в цикле. Этот прием отлично работает, если время выполнения остальной части программы невелико.

Но предположим, вы устраняете дребезг в цикле, выполнение которого занимает значительное время. Например, в основном цикле программы изменяется яркость

Рис. 12.1. Влияние внешнего прерывания на ход выполнения программы

- 255 -

светодиода или скорость двигателя с помощью оператора for() с некоторой задержкой delay(). Возникает опасность пропустить нажатие кнопки, которое происходит в момент выполнения главной программы. Вот здесь и приходят на помощь прерывания. Определенные контакты на плате Arduino могут вызывать внешние аппаратные прерывания. Вы выполняете главную программу, и при возникновении внешнего прерывания запускается специальная процедура его обработки (рис. 12.1), причем прерывание может наступить в любом месте программы.

 

12.2. Что выбрать: опрос состояния в цикле или прерывания?

Аппаратные прерывания являются альтернативой опроса состояния входов в цикле loop(). Они не лучше и не хуже, всегда есть выбор между ними. При проектировании системы необходимо учитывать все факторы и выбрать вариант, наиболее подходящий для вашего приложения. Далее рассмотрим основные различия между опросом входов и прерываниями, чтобы понять, что лучше подойдет для конкретного проекта.

12.2.1. Программная реализация

Благодаря встроенному языку программирования Arduino программировать внешние прерывания сравнительно просто. Однако организовать опрос контактов в цикле еще проще, все, что требуется - это вызов команды digitalRead(). Если нет безусловной необходимости в аппаратных прерываниях, то лучше их не применять, т. к. код программы усложнится.

12.2.2. Аппаратная реализация

С точки зрения аппаратной реализации между опросом контакта в цикле и прерыванием нет разницы, т. к. в обоих случаях считывается состояние входа. Тем не менее, при наличии дребезга ( см. главу 2) возникает серьезная проблема: процедура обработки прерывания может быть вызвана несколько раз. Самое неприятное, что в процедуре обработки прерывания нельзя задействовать функцию программного устранения дребезга, потому что невозможен вызов функции ctelay(). Поэтому, если вы хотите использовать прерывание для входа с дребезгом, необходимо устранить дребезг аппаратно.

12.2.3. Многозадачность

Одна из причин использования прерываний - предоставление псевдомногозадачности. С помощью Arduino никогда нельзя обеспечить реальную многозадачность, т. к. на плате только один микроконтроллер, который за один такт может выполнить только одну команду. Однако, поскольку микроконтроллер выполняет команды очень быстро, можно прибегнугь к прерываниям, чтобы выполнять разные задачи почти одновременно. Например, с помощью прерывания можно в про-

- 256 -

цессе уменьшения яркости светодиодов реагировать на нажатие кнопки, которая регулирует скорость или цвет. В то время как при опросе контакта в цикле можно прочитать значение входа кнопки командой dig i talRead() только один раз и имеющиеся в цикле loop() "медленные" операции снижают эффективность контроля входа кнопки.

12.2.4. Точность сбора данных

Для некоторых задач, где требуется быстрый сбор данных, прерывания являются необходимостью. Один из примеров - энкодер, смонтированный на двигателе постоянного тока, отправляющий в микроконтроллер импульс при определенном числе оборотов вала. Так осуществляется следящая обратная связь за скоростью вращения двигателя постоянного тока. Это позволяет динамически регулировать скорость в зависимости от нагрузки или отслеживать поворот вала двигателя. Но в такой системе исключительно важно быть уверенным, что плата Arduino получает каждый импульс. Поскольку импульсы очень короткие (намного короче импульса при нажатии кнопки), то при опросе в цикле loop() их можно пропустить. Если энкодер посылает импульс два раза за оборот вала двигателя, то из-за пропуска одного импульса измеренная частота вращения окажется вдвое меньше истинной. Поэтому в данном случае без аппаратного прерывания не обойтись.

12.2.5. Реализация аппаратного прерывания в Arduino

В большинстве плат Arduino для аппаратных прерываний выделены специальные контакты. Прерывания обозначаются с помощью идентификационного номера, которому сопоставлен определенный контакт (табл. 12.1). Исключение составляет плата Due, у которой для прерываний можно задействовать любой контакт, и номер прерывания совпадает с номером контакта.

Таблица 12.1. Аппаратные прерывания на различных платах Arduino

Плата INTO INT1 INT2 INT3 INT4 INT5
UNO, Ethernet Pin2 Pin3 - - - -
Mega2560 Pin2 Pin3 Pin21 Pin20 Pin19 Pin18
Leonardo Pin3 Pin2 Pin0 Pin1 - -

Для вызова прерывания существует встроенная функция attachInterrupt(). Ее первый аргумент - это идентификатор прерывания (для плат из табл. 12.1) или номер контакта Arduino (для платы Due). Если на плате Arduino Uno вы хотите назначить прерывание физическому контакту 2, то первый аргумент функции attachInterrupt() будет 0. Arduino Uno (и другие платы на базе ATmega328) поддерживает всего два внешних прерывания, платы Mega и Leonardo больше (см. табл. 12.1).

- 257 -

Аппаратные прерывания осуществляют переход к выполненшо определенных функций. Второй аргумент функции attach_interrupt() - имя выполняемой функции. Если вы хотите переключать состояние логической переменной при каждом прерывании, то функция обработки прерывания будет выглядеть так:

void toggleLed()

{

var = !var;

}

При вызове этой функции переменная var переключается в состояние, противоположное предыдущему, и происходит возврат в основную программу. Последний аргумент функции attachInterrupt() - режим запуска прерывания. Существуют режимы LOW, CHANGE, RISING и FALLING (для платы Due также есть режим HIGH).

Режимы CHANGE, RISING и FALLING наиболее популярны, потому что прерывание выполняется только один раз при изменении состояния входа. Режимы HIGH и LOW встречаются реже, потому что будут вызывать процедуру обработки прерывания непрерывно, блокируя остальную часть программы.

 

12.3. Разработка и тестирование системы противодребезговой защиты для кнопки

Чтобы применить полученные знания на практике, построим схему управления RGB-светодиодом с помощью кнопки с аппаратной противодребезговой защитой.

Пусть яркость одного из компонентов цвета RGB-светодиода меняется от максимума к минимуму и наоборот. При нажатии кнопки цвет меняется на другой, в то время как при изменении яркости используется функция задержки delay().

12.3.1. Создание схемы аппаратного устранения дребезга

Как вы узнали в главе 2, при нажатии кнопок уровень напряжения на входе многократно меняется от максимального до минимального значения. При наличии аппаратных прерываний это представляет большую проблему, потому что программа обработки прерывания будет вызвана несколько раз. К счастью, дребезг можно устранить аппаратно, получая на входе неискаженный сигнал.

Обычная кнопка подключена к цифровому выводу с помощью подтягивающего резистора. Наличие подтягивающего резистора (pull-up) дает следующий эффект: по умолчанию на входе высокий уровень, при нажатии на кнопку контакт Arduino соединяется с землей, и на вход платы поступает низкий уровень.

На рис. 12.2 изображена осциллограмма сигнала на входе при нажатии кнопки.

Видно, что напряжение скачет несколько раз вверх и вниз, прежде чем устанавливается в низком состоянии.

При таком сигнале функция обработки прерывания будет вызвана три раза подряд.

Чтобы предотвратить это, добавим в схему RC-цепочку. Подключим параллельно

- 258 -

кнопке конденсатор, а последовательно подсоединим резистор (рис. 12.3). Когда кнопка не нажата, конденсатор заряжается через резисторы R1 и R2. При нажатии на кнопку конденсатор начинает разряжаться и через некоторое время на выходе напряжение упадет до нуля. Если есть дребезг, сигнал "подпрыгивает" вверх и вниз в течение нескольких миллисекунд, но конденсатор подзаряжается через резистор и на выходе поддерживается высокий уровень напряжения. Благодаря этому уровень сигнала меняется с высокого на низкий только один раз в течение интервала времени, определяемого значениями резистора и конденсатора.

Рис. 12.2. Дребезг при нажатии обычной кнопки

Рис. 12.3. Схема устранения дребезга с помощью RC-цепочки

- 259 -

Резистор R2, включенный последовательно с кнопкой, уменьшает ток заряда и разряда конденсатора. Слишком большой ток может повредить кнопку. Добавление резистора 100 Ом увеличивает время разряда и обеспечивает безопасность компонентов. Но в результате спад импульса приобретает вид, изображенный на рис. 12.4.

Погашение дребезга с помощью RC-контура

Рис. 12.4. Сигнал на выходе RC-цепочки, устраняющей дребезг

Благодаря RC-цепочке дребезг исчез, но перепад напряжения на выводе Arduino приобрел экспоненциальную форму. При опросе контакта прерывание происходит при переходе от низкого уровня к высокому и от высокого к низкому с определеой скоростью. Из-за сглаживания, вызванного конденсатором, момент прерывания определяется неточно. Крутизну спада сигнала можно увеличить с помощью триггера Шмитта. Интегральные схемы с триггером Шмитта обеспечивают резкий перепад сигнала на выходе, при превышении входным сигналом определенного порога. Выходной сигнал с триггера Шмитта можно непосредственно подавать на контакт Arduino. В этой главе мы используем инвертирующий триггер Шмитта на микросхеме 74НС14. В корпусе этой микросхемы находятся шесть отдельных инвертирующих триггеров Шмитта, в наших примерах понадобится только один.

Цоколевка микросхемы приведена на рис. 12.5.

Подсоединим выход RC-цепочки к входу триггера Шмитта, а сигнал с его выхода подадим на контакт платы Arduino (рис.-12.6).

Поскольку выход триггера инверсный, то и сигнал будет перевернут. При нажатии кнопки уровень на входе Arduino будет высокий. При написании программы необходимо помнить об этом. Окончательный вид выходного сигнала изображен на рис. 12.7, как видим, сигнал чистый, без дребезга. Такой сигнал вполне подходит для формирования аппаратного прерывания.

- 260 -

Рис. 12.5. Цоколевка микросхемы 74НС14

Рис. 12.6. Окончательный вариант схемы подавления дребезга

12.3.2. Монтаж схемы

Мы разобрались, как работает аппаратное подавление дребезга для кнопки. Для экспериментов возьмем RGB-светодиод и одну кнопку. Соедините элементы согласно схеме, изображенной на рис. 12.8.

- 261 -

Погашение дребезга с помощью RC-контура и триггера Шмитта

Рис. 12.7. Сигнал на выходе схемы подавления дребезга с триггером Шмипа

Рис. 12.8. Схема устройства подавления дребезга

- 262 -

12.3.3. Программа обработки аппаратного прерывания

Пришло время написать простую программу для проверки работы устройства устранения дребезга и уяснения возможностей аппаратных прерываний Arduino. Наиболее очевидное и полезное применение аппаратных прерываний - опрос внешних входов во время работы программы, в которой присутствуют задержки delay().

Есть много сценариев, в которых это необходимо, мы будем управлять яркостью светодиода, формируя ШИМ-сигнал с помощью функции analogWrite(). Устройство подает на один из трех выводов RGB-светодиода сигнал, варьирующий от 0 до 255 и обратно. Каждый раз при нажатии на кнопку меняется цвет свечения. Это невозможно сделать с помощью опроса состояния вывода в цикле, т. к. момент нажатия кнопки почти наверняка будет пропущен.

Сначала разберемся, что такое переменные volatile. Если переменная будет меняться при выполнении прерывания, ее следует объявить как volatile (нестабильная). Это необходимо для корректной обработки переменной компилятором. Пример объявления переменной как volatile:

volatile int selectedLED = 9;

Для вызова прерывания в Arduino предусмотрена функция attachInterrupt(), находящаяся в setup(). Параметры функции: идентификатор прерывания (или номер контакта для платы Due ), имя функции обработки прерывания и режим запуска прерывания (LOW, CHANGE, RISING или FALLING). В нашей программе объявлено прерывание о (вывод 2 на плате Uno), которое при срабатывании RISING (по фронту) запускает функцию swap()

attachInterrupt(0, swap, RISING);

Осталось написать код функции обработки прерывания swap() и добавить его к основной программе. После подключения прерывания и написания кода функции обработки прерывания вы можете писать остальной текст программы. Каждый раз при вызове прерывания основная программа приостанавливается, выполняется функция обработки прерывания, затем ход основной программы продолжается с того места, где она была прервана. Поскольку прерывание останавливает основную программу, его обработка должна быть очень короткой и не содержать задержки delay(). Теперь все готово для написания программы управления яркостью RGB-светодиода и переключения цвета по нажатию кнопки. Полный текст программы приведен в листинге 12.1.

Листинг 12.1. Аппаратные прерывания, реализующие многозадачность - hw_multitask.ino

// кнопка с аппаратной противодребезговой защитой,

// управляемая прерыванием

// Контакт кнопки

const int BUTTON_INT =0; // Прерывание 0 (вывод 2 для Uno)

const int RED=11;

// Красный вывод RGB-светодиода контакт 11

const int GREEN=10;

// Зеленый вывод RGB-светодиода контакт 10

const int BLUE=9;

// Синий вывод RGB-светодиода контакт 9

- 263 -

// Переменные volatile можно изменять внутри функции обработки прерывания

volatile int selectedLED = RED;

void setup()

{

pinMode (RED, OUTPUT);

pinMode (GREEN, OUTPUT);

pinMode (BLUE, OUTPUT);

// Режим прерывания RISING (переход с LOW на HIGH)

attachInterrupt(BUTTON_INT, swap, RISING);

}

void swap()

{

// Выключить текущий цвет

analogWrite(selectedLED, 0);

// Новое значение для переменной selectedLED

if (selectedLED == GREEN)

selectedLED = RED;

else if (selectedLED == RED)

selectedLED = BLUE;

else if (selectedLED == BLUE)

selectedLED = GREEN;

}

void loop()

{

for (int i = 0; i<256; i++)

{

analogWrite(selectedLED, i);

delay(10);

}

for (int i = 255; i>= 0; i--)

{

analogWrite(selectedLED, i);

delay(10);

}

}

Загрузите программу на плату Arduino, вы увидите изменение яркости одного из цветов (R, G или В) RGB-светодиода от нуля до максимума и обратно. Каждый раз при нажатии на кнопку выбирается следующий цвет с той же яркостью, что и предыдущий.

ПРИМЕЧАНИЕ

Посмотреть видеоурок, демонстрирующий пример аппаратного прерывания Arduino для кнопки без дребезга, можно на странице http://www.exploringarduino.com/content/ch12. Этот видеофайл доступен также на сайте издательства Wiley.

- 264 -

 

12.4. Прерывания по таймеру

Аппаратные прерывания - не единственный вид прерываний, который возможен для Arduino. Существуют также прерывания по таймеру. В контроллере ATmega328 (установленном на Arduino Uno) есть три аппаратных таймера. На самом деле Arduino по умолчанию использует эти таймеры для функции millis(), работы с delay() и для включения ШИМ при вызове analogWrite(). Хотя в языке программирования Arduino отсутствуют специальные конструкции для работы с таймерами, управляя таймерами вручную, можно генерировать произвольные ШИМ-сигналы на любом контакте и делать многое другое. Далее мы расскажем, как с помощью сторонних библиотек (библиотека TimerOne) управлять 16-разрядным таймером Timer1 в ATmega328. Подобные библиотеки есть и для плат Leonardo, но здесь опишем только работу с Arduino Uno.

ПРИМЕЧАНИЕ

Таймер Timer1 служит для включения ШИМ-сигнала на выводах 9 и 10, поэтому при работе с библиотекой TimerOne выполнить функцию analogWrite() для этих контактов не удастся.

12.4.1. Общие сведения о прерываниях по таймеру

Так же, как секундомер в ваших часах, таймеры в Arduino начинают отсчет с нуля, увеличивая значение с каждым тактом кварцевого резонатора. Timer1 16-разрядный таймер, следовательно, значения в нем меняются от 0 до 216 - 1 (или 65 535). Как только это число будет достигнуто, значения сбрасываются в 0 и отсчет начинается снова. Время, через которое таймер достигает максимального значения, зависит от делителя частоты. Поскольку тактовая частота равна 16 МГц, то при отсутствии делителя переполнение и сброс Timer1 произойдет много раз в секунду. Библиотека TimerOne берет на себя все нюансы работы с таймером и позволяет установить любой интервал времени в микросекундах для срабатывания прерывания по таймеру.

12.4.2. Установка библиотеки

Для начала загрузите библиотеку TimerOne либо со страницы сайта Wiley для этой главы, либо непосредственно со страницы https://code.google.com/p/arduinotimerone/downloads. Распакуйте архив в папку с названием TimerOne и скопируйте в папку библиотек Arduino. Размещение этой папки по умолчанию отличается для разных операционных систем:

• Windows - Documents/ Arduino/libraries;

• Mac - Documents/ Arduino/libraries;

• Linux - /homeNOUR USER NAME/sketchbook/libraries.

Если в момент распаковки архива и копирования папки TimerOne среда Arduino IDE была открыта, перезапустите ее и убедитесь, что библиотека загрузилась.

Теперь все готово для работы с Timer1 на Arduino.

- 265 -

12.4.3. Одновременное выполнение двух задач

Важно понимать, что с помощью Arduino нельзя реализовать "истинную" многозадачность. Прерывания создают лишь видимость одновременного выполнения нескольких операций, позволяя переключаться между несколькими задачами чрезвычайно быстро. Использование библиотеки TimerOne позволит заставить мигать светодиод по таймеру, пока в цикле loop() выполняются другие задачи. В конце главы мы опишем проект, где в основном цикле loop() данные выводятся в последовательный порт с задержкой delay(), а по прерываниям таймера осуществляется управление светодиодом и пьезозуммером. Чтобы убедиться, что библиотека работает правильно, можно загрузить программу, приведенную в листинге 12.2, на плату Arduino Uno. Светодиод, подключенный к контакту 13 платы Arduino, будет мигать раз в секунду и управляться с помощью таймера. Если вставить какой-либо фрагмент кода в цикл loop(), то он будет выполняться одновременно.

// Использование прерывания по таймеру

Листинг 12.2. Прерывания по таймеру для проврки blink-timer1.ino

#include

const int LED=13;

void setup()

{

pinMode(LED, OUTPUT);

Timer1.initialize(1000000); // Значение для переполнения 1000000 мкс

// (1 секунда)

Timer1.attachInterrupt(blinky); // Выполнять blinky()при переполнении

}

void loop()

{

// Вставьте сюда любой код

}

// Обработка прерываний по таймеру

void blinky()

{

digitalWrite(LED, !digitalRead(LED)); // Переключить статус светодиода

}

В функции Timer1.initialize() задается временной интервал таймера в микросекундах. В этом примере установлено значение 1 с (1000000 мкс). Аргумент команды Timer1.attachInterrupt() - имя функции, которая будет выполняться по истечении заданного интервала времени. Очевидно, что функция обработки займет меньше времени, чем интервал между прерываниями.

- 266 -

 

12.5. Музыкальный инструмент на прерываниях

Чтобы закрепить знание аппаратных прерываний и прерываний по таймеру, создадим электронный музыкальный инструмент, который воспроизводит ноты последовательно в нескольких октавах. Ноту выбирают с помощью кнопки с аппаратной противодребезговой защитой. По прерываниям таймера одна и та же нота по порядку воспроизводится сначала в октаве 2, затем 3 и так до октавы 6, пока нажатием кнопки не будет выбрана следующая нота. В цикле loop() для отладки можно предусмотреть вывод текущей ноты и октавы через последовательный интерфейс на экран компьютера.

Если известна начальная частота, несложно вычислить частоты нот для каждой октавы. Например, рассмотрим ноту (До). Нота До большой октавы (С2) имеет частоту около 65 Гц. Чтобы получить частоту для До малой октавы СЗ (130 Гц) умножим частоту С2 на 2. Чтобы получить частоту для С4 (260 Гц), умножим частоту для СЗ на 2. Таким образом, частоту ноты для каждой октавы можно вычислить, умножая исходную частоту на степень числа 2. Зная это правило, будем увеличивать коэффициент в два раза при прерываниях по таймеру.

Переключать ноты будем так же, как в предьщущем примере с цветным светодиодом, - по нажатию кнопки. Задаем базовые частоты для каждой ноты и переключаем их каждый раз при нажатии кнопки.

12.5.1. Схема музыкального инструмента

Аппаратная часть проекта очень простая. К схеме предыдущего примера с RGB-светодиодом добавляем динамик, подключенный к контакту 12 Arduino через резистор 150 Ом. Я взял пьезоизлучатель, вы можете выбрать другой динамик. Монтажная схема показана на рис. 12.9.

12.5.2. Программа для музыкального инструмента

Программа для музыкального инструмента использует аппаратные прерывания и прерывания по таймеру, а также функцию tone() для управления динамиком и осуществляет передачу данных в последовательный порт. Загрузите код из листинга 12.3 на плату Arduino и нажимайте кнопку для перебора базовых частот. Посмотреть частоту, воспроизводимую в текущий момент, можно в мониторе последовательного порта.

Листинг 12.3. Код для музыкального инструмента - fun_with_sound.ino

// Использование аппаратных прерываний и прерываний по таймеру

// Подключение библиотеки TimerOne

#include

//

const int BUTTON INT =0; // Прерывание 0 (вывод 2 для Uno)

const int SPEAKER=12;

// Вывод 12 для подключения динамика

- 267 -

Рис. 12.9. Графическая схема музыкального аппарата

// Базовые частоты для нот

#define NOTE_C 65

#define NOTE_D 73

#define NOTE_E 82

#define NOTE_F 87

#define NOTE_G 98

#define NOTE_A 110

#define NOTE_B 123

// Переменные volatile для изменения при обработке прерываний

volatile int key = NOTE_C;

volatile int octave_multiplier = 1;

void setup()

{

// Запуск последовательного порта

Serial.begin(9600);

pinMode (SPEAKER, OUTPUT);

// Запуск для кнопки аппаратного прерывания по RISING

attachInterrupt(BUTTON_INT, changeKey, RISING);

//Set up timer interrupt

Timer1.initialize(500000);

// Период 0,5 секунды

- 268 -

Timer1.attachInterrupt(changePitch); // Выполнять changePitch()

// при прерываниях по таймеру

void changeKey()

{

octave_multiplier=1;

if (key == NOTE_C)

key = NOTE_D;

else if (key == NOTE_D)

key = NOTE_E;

else if (key == NOTE_E)

key = NOTE_F;

else if (key == NOTE_F)

key = NOTE G;

else if (key == NOTE_G)

key = NOTE_A;

else if (key == NOTE_A)

key = NOTE_B;

else if (key == NOTE_B)

key = NOTE_C;

}

// Обработка прерывания по таймеру

void changePitch()

{

octave_multiplier = octave_multiplier * 2;

if (octave_multiplier > 16) octave_multiplier

tone(SPEAKER,key*octave_multiplier);

}

void loop()

{

Serial.print ("Кеу: ");

Serial.print(key);

Serial.print(" Multiplier: ");

Serial.print(octave_multiplier);

Serial.print(" Frequency: ");

Serial.println(key*octave_multiplier);

delay(100);

}

Значения частот для нот можно найти в Интернете. Базовые частоты определены для нот второй октавы. Обратите внимание, что переменные key и octave_multiplier должны быть объявлены как volatile, потому что их значения меняются во время обработки прерываний. Функция changeKey() вызывается каждый раз при срабатывании прерывания по нажатию кнопки и изменяет базовое значение пере

- 269 -

менной key. Функция changePitch() вызывает tone() для установки частоты сигнала для динамика. Она запускается каждые полсекунды по прерыванию таймера.

При каждом вызове она удваивает базовую частоту, пока не будет достигнуто ее 16-кратное увеличение. Затем процесс повторяется с начальной базовой частоты.

В цикле loop() в последовательный монитор каждые 0, 1 с выводятся значения переменных key, octave_multiplier и частоты.

ПРИМЕЧАНИЕ

Посмотреть видеоклип, демонстрирующий работу музыкального инструмента, можно на странице www.exploringarduino.com/content/ch12. Этот видеофайл доступен и на странице издательства Wiley.

Резюме

В этой главе вы узнали следующее:

• Как выбрать альтернативу: опрос входов в цикле или использование прерываний.

• Что реализация прерываний на различных платах Arduino неодинакова. На плате Due прерывания можно задать для любого контакта, на других платах для прерываний доступны только определенные контакты.

• Как создать противодребезговую защиту кнопок на аппаратном уровне с помощью RC-цепочки и триггера Шмитта.

• Что с помощью функций обработки прерываний можно асинхронно опрашивать входы Arduino.

• Как с помощью сторонней библиотеки TimerOne организовать прерывания по таймеру.

• Как комбинировать прерывания таймера, аппаратные прерывания и опрос в одной программе, чтобы параллельно выполнять несколько задач.

 

Глава 13. Обмен данными с картами памяти SD

Список деталей

Для повторения примеров главы вам понадобятся следующие детали:

• плата Arduino (рекомендуется Uno );

• USB-кабель для программирования платы Arduino;

• источник питания для платы Arduino (блок питания или аккумулятор);

• инфракрасный датчик расстояния;

• плата часов реального времени;

• плата расширения SD card shield;

• карта памяти SD;

• набор перемычек;

• макетная плата;

• компьютер с картридером.

Электронные ресурсы к главе

На странице http://www.exploringarduino.com/content/ch13 можно загрузить программный код, видеоуроки и другие материалы для данной главы. Кроме того, листинги примеров можно скачать со страницы www.wiley.com/go/exploringarduino в разделе Downloads.

Что вы узнаете в этой главе

Есть множество примеров устройств на основе плат Arduino для сбора данных о состоянии атмосферы с метеодатчиков, зондов, датчиков систем жизнеобеспечения зданий и т. п. Учитывая небольшой размер, минимальное энергопотребление и простоту взаимодействия с датчиками, платы Arduino удобны для построения регистраторов данных (устройств для записи и хранения информации в течение определенного периода времени). Такие регистраторы часто присутствуют в сетях сбора данных и для хранения накопленной информации требуется энергонезависимая память, например SD-карта. В этой главе вы узнаете, как связать Arduino с SD-картой.

- 271 -

Мы расскажем, как записывать данные в файл и как читать информацию с SD-карты. С помощью часов реального времени к данным мы добавим точные временные метки. Вы также узнаете о том, как отобразить эти данные на вашем компьютере.

ПРИМЕЧАНИЕ

Видеоурок по записи и хранению данных можно посмотреть на странице

http://www.jeremyblum.com/2011 /04/05/tutorial-11-for-arduino-sd-cards-anddatalogging/ .

ПРИМЕЧАНИЕ

Видеоурок о регистрации данных местоположения с помощью GPS-приемника можно посмотреть на странице http://www.jeremyblum.com/2012/07/16/tutorial-15-for-arduinogps-tracking/. Эти видеофайлы доступны и на странице издательства Wiley.

 

13.1. Подготовка к регистрации данных

Системы регистрации данных, как правило, предназначены для сбора информации, поступающей, например, с аналоговых датчиков. Неотьемлемая часть подобных систем - устройства памяти для хранения данных в течение длительного времени.

Далее рассмотрим несколько способов использования SD-карты для записи данных с платы Arduino. Кратко перечислим возможные применения регистраторов:

• метеостанция для мониторинга освещенности, температуры и влажности в течение длительного времени;

• GPS-трекер для отслеживания местоположения;

• система контроля за температурой компонентов персонального компьютера;

• регистратор для управления электрическим освещением в доме или офисе.

В конце главы мы разработаем систему протоколирования данных с инфракрасным датчиком расстояния, которая позволит создать журнал входа и выхода людей из помещения.

13.1.1. Форматирование данных с помощью CSV-файлов

Хранить данные на SD-карте будем в CSV-файлах. Файлы этого формата легко создать, считать и проанализировать в различных приложениях, что хорошо подходит для задач регистрации информации. Стандартный CSV-файл выглядит примерно так:

Date,Time,Valuel,Value2

2013-05-15,12:00,125,255

2013-05-15,12:30,100,200

2013-05-15,13:00,110,215

- 272 -

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

Таблица 13.1. Просмотр CSV-файла в программе Excel

Поскольку CSV-файлы являются простыми текстовыми файлами, записывать в них данные можно с помощью знакомых нам команд print() и println(). С помощью Arduino можно легко проанализировать CSV -файлы, считывая их по строкам и отделяя нужную информацию от разделителей.

13.1.2. Подготовка SD-карты для регистрации данных

Перед тем как начать запись данных с Arduino, необходимо определиться с SD-картой. Вид SD-карты зависит от SD-адаптера, который вы будете использовать.

Существуют полноразмерные SD-карты и microSD. Большинство microSD-карт поставляется с адаптером, который позволяет подключить их к считывателю стандартных SD-карт. Для выполнения упражнений из этой главы вам потребуется картридер для компьютера (внешний или встроенный).

Большинство новых SD-карт заранее отформатировано и готово к использованию с Arduino. Если на вашей карте была записана какая-нибудь информация, необходимо ее отформатировать в FAT16 или FAT32. Карты объемом до 2 Гбайт следует отформатировать в FAT16, карты с большим объемом памяти- в FAT32. В примерах этой главы мы применяем microSD-карту, отформатированную в FАТ16. Обратите внимание, что при форматировании карты удаляется вся имеющаяся на ней информация, но при этом гарантируется, что карта готова для взаимодействия с Arduino. Если у вас новая карта, следующие шаги можно пропустить и вернуться к ним только при возникновении проблем с доступом к карте из Arduino.

Форматировать SD-карту в Windows легко:

1. Вставьте SD-карту в картридер. Откройте окно Computer (Мой компьютер) (рис. 13.1 ).

2. Щелкните правой кнопкой мыши по значку SD-карты ( она может иметь другое имя) и выберите опцию Format (рис. 13.2). Появится окно с вариантами форматирования карты (рис. 13.3).

3. Выберите тип файловой системы (FAT для карт 2 Гбайт и меньше, FAT32 для карт большего объема), оставьте размер кластера по умолчанию и задайте метку

- 273 -

Рис. 13.1. SD-карта в окне Computer

тома (я выбрал LOG, но вы можете выбрать любую). На рис. 13.3 изображена конфигурация форматирования для карты 2 Гбайт.

4. Нажмите кнопку Start, чтобы отформатировать карту памяти.

На компьютерах с ОС Mac процесс также прост:

1. Используйте команду Finder, чтобы найти и открыть приложение Disk Utility.

2. Нажмите на вкладку SDcard в левой панели и откройте вкладку Erase. Выберите пункт MS-DOS (FAT) для формата.

3. Нажмите кнопку Erase. Система отформатирует карту как FАТ16, независимо от ее емкости (в Mac нельзя отформатировать карту как FAT32).

В Linux можно отформатировать SD-карту из терминала. Большинство дистрибутивов Linux будет монтировать карту автоматически при ее вставке:

1. Вставьте карту, откроется всплывающее окно, отображающее карту.

2. Откройте терминал и введите команду df, чтобы получить список смонтироваых устройств. Результат должен выглядеть так, как на рис. 13.4.

Последняя запись должна относиться к SD-карте. На моей системе она была смонтирована как /ctev /mmcblkOp1, но у вас может отличаться.

- 274 -

Рис. 13.2. Выбор опции форматирования SD-карты

Рис. 13.3. Окно опций форматирования

- 275 -

Рис. 13.4. Список смонтированных устройств

3. Прежде чем форматировать SD-карту, необходимо ее отмонтировать с помощью команды umount. Аргументом команды будет название вашей SD-карты (рис. 13.5).

Рис. 13.5. Отмонтирование SD-карты в Linux

4. Форматируем SD-карту с помощью команды mkdosfs. Возможно, вам придется выполнить команду с правами привилегированного пользователя ( с помощью команды sudo). Используем флаг -F, чтобы указать файловую систему FAT. Вы можете включить опции 16 или 32, чтобы выбрать FAT16 или FAT32. Отформа-

- 276 -

тировать карту, которая была смонтирована как /dev/mmcblkOp1, можно командой sudo mkdosfs -F 16 /dev/mmcblkOp1 (рис. 13.6).

Ваша SD-карта отформатирована и готова к работе! Приступаем к взаимодействию с SD-картой через плату SD card shield.

Рис. 13.6. Форматирование SD-карты в Linux

 

13.2. Взаимодействие Arduino с SD-картой

Для SD-карт, как и для радиомодулей ХВее, которые мы рассмотрели в главе 11, требуется питание 3,3 В. Поэтому подключать SD-карту необходимо через переходник, который преобразует логические уровни и напряжение питания для SD-карты. Кроме того, связь с SD-картой возможна по интерфейсу SPI, описанному в главе 9. Arduino IDE поставляется с удобной библиотекой SD, реализующей функции низкого уровня и позволяющей легко читать и записывать файлы на SD-карту.

13.2.1. Платы расширения для SD-карт

Есть множество плат расширения (переходников) для подключения SD-карт к Arduino. Все рассмотреть в книге невозможно, опишем некоторые из популярных, указав их достоинства и недостатки.

Общие черты всех переходников SD-карт:

• подключаются к плате Arduino по интерфейсу SPI, через 6 контактов ICSP платы Arduino или к контактам цифровых выводов (11, 12 и 13 на Uno или 50, 51 и 52 на Mega);

- 277 -

• имеют вход выбора (CS), который может быть контактом по умолчанию (53 на Mega, 10 на других платах Arduino );

• подают питание 3,3 В на SD-карту и согласуют логические уровни.

Вот список наиболее распространенных плат расширения для SD-карт:

• Cooking Hacks Micro SD shield (http://www.exploringarduino.com/parts/cooking-hacks-SD-shield) (рис. 13. 7) используется для иллюстрации примеров этой главы. Это самый маленький переходник из рассматриваемых нами, и его можно подключить либо к цифровым контактам (8-13 на Uno ), либо к 6-контактному ICSP на Arduino. При подключении к контактам 8-13 вход выбора CS соединен с контактом 10. При подключении к разъему ICSP вход CS можно соединить с любым контактом Arduino. Это полезно, если контакт 10 платы Arduino занят. Переходник поставляется с SD-картой 2 Гбайт.

Рис. 13.7. Плата расширения Cooking Hacks Micro SD shield

• Official Arduino Wireless SD shield (http://www.exploringarduino.com/parts/arduino-wireless-shield) (рис. 13.8)- это первая из нескольких "официальных" плат расширения Arduino с поддержкой SD-карт. Переходник содержит схему для подключения радиомодуля ХВее и SD-карты памяти, что позволяет легко объединить примеры из настоящий главы и из главы 11. На этой плате расширения вход выбора SD-карты (CS) подключается к контакту 4 Arduino. Вы должны назначить контакт 10 как выход, а также указать в библиотеке, что контакт 4 подключен к CS.

• Official Arduino Ethernet SD shield (http://www.exploringarduino.com/parts/arduino-ethernet-shield) (рис. 13.9) позволяет подключить плату Arduino к проводной сети. Переходник также реализует интерфейс SD-карты, хотя в основном он служит для хранения файлов, которые будут доступны через сеть. Оба контроллера (Ethernet и SD-карты) на этом переходнике являются SPI-устрой

- 278 -

Рис. 13.8. Плата расширения Arduino Wireless SD shield

Рис. 13.9. Плата расширения Arduino Ethernet SD shield

ствами; вывод CS Ethemet подключается к контакту 10 платы Arduino, а вывод CS SD-карты - к контакту 4.

• Official Arduino Wi-Fi SD shield (http://www.exploringarduino.com/parts/arduino-wifi-shield) (рис. 13.10) также реализует подключение к сети, но через Wi-Fi. SD-карта, как и в Ethemet SD shield, служит для хранения файлов, доступных через сеть. Как и в Ethemet SD shield, Wi-Fi-контроллер для CS исполь

- 279 -

зует контакт 10, а SD-карта - контакт 4. Необходимо следить, чтобы оба устройства не были включены одновременно; активной может быть только одна линия CS (низкий логический уровень).

Adafruit data logging shield (http://www.exploringarduino.com/parts/ adafruitdata-logging-shield) (рис. 13. 11) особенно хорошо подходит для экспериментов,

Рис. 13.10. Плата расширения Arduino Wi-Fi SD shield

Рис. 13.11. Плата расширения Adafruit data logging shield

- 280 -

которые мы рассмотрим далее в этой главе, потому что включает в себя часы реального времени (RTC) и интерфейс SD-карты. Вывод CS SD-карты на этом переходнике назначен по умолчанию (53 на Mega, 10- на других платах Arduino ), а чип часов реального времени подключен к шине I2C.

На плате расширения SparkFun MicroSD shield (http://www.exploringarduino.com/parts/spark-fun-microSD-shield) (рис. 13.12) установлен только слот SD-карты. Тем не менее, предусмотрено монтажное поле для припаивания дополнительных компонентов. Вывод CS SD-карты подключен к контакту 8, его необходимо указать при использовании библиотеки SD с этим переходником.

Рис. 13.12. Плата расширения SparkFun MicroSD shield

13.2.2. SPI-интерфейс SD-карты

Как упоминалось ранее, связь платы Arduino с SD-картой осуществляется через SPI-интерфейс: контакты MOSI, MISO, SCLK (тактовые импульсы) и CS (выбор чипа). Для выполнения примеров этой главы мы будем использовать библиотеку SD. Это предполагает, что задействованы аппаратные SPI-контакты на плате Arduino, а в качестве CS назначен или контакт по умолчанию или определенный пользователем. Библиотека SD для правильной работы требует установки контакта для CS по умолчанию в качестве выхода, даже если для CS задан другой вывод.

В случае Uno это вывод 10, для платы Mega это вывод 53. Описанные далее примеры собраны на плате Arduino Uno с выводом CS, заданным по умолчанию.

13.2.3. Запись на SD-карту

Библиотека SD позволит создать несложный пример записи данных на SD-карту.

Будем получать данные с датчиков и записывать на карту. Данные хранятся в фай

- 281 -

ле под названием log.csv, в дальнейшем его можно открыть на компьютере. Важно отметить, что при форматировании карты в F АТ 16 имена файлов должны быть записаны в формате "8.3", т. е. расширение должно состоять из трех символов, а имя файла - не более чем из восьми символов.

Убедитесь, что переходник SD правильно подключен к плате Arduino и в него установлена SD-карта. Для Cooking Hacks Micro SD shield это будет выглядеть так, как на рис. 13.13 (используются контакты 8-13 Arduino и перемычка находится на правой стороне).

Рис. 13.13. Подключение платы расширения Cooking Hacks Micro SD shield

Для отладки программы будем контролировать статус нескольких функций библиотеки SD. Например, для установки связи с SD-картой необходимо вызвать следующую функцию (листинг 13.1 ):

Листинг 13.1. Функция инициализации SD-карты

if (!SD.begin(CS_pin))

{

Serial.println("Card Failure");

return;

}

Serial.println("Card Ready");

- 282 -

Обратите внимание, что мы не просто инициализируем обмен с картой с помощью функции so.begin (CS_pin), а получаем статус выполнения этой функции. При успешной инициализации программа выдает в последовательный порт сообщение об этом, в противном случае выводится сообщение о неуспехе и команда возврата останавливает дальнейшее выполнение программы.

При записи строки в файл подход аналогичный. Например, если вы хотите записать новую строку "hello" в файл, код будет выглядеть так, как в листинге 13.2.

Листинг 13.2. Функция записи информации на SD-карту

File dataFile = SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.println("hello");

dataFile.close();

// Данные не записываются,

// пока соединение не будет закрыто

}

else

{

Serial.println("Couldn't open log file");

}

В первой строке расположена команда создания нового файла (или открытие, если он уже существует) с названием log. csv. Если файл создан/открыт успешно, переменная dataFile получит значение true, и начнем процесс записи данных в файл.

В противном случае сообщаем об ошибке в последовательный порт. Запись строки данных в файл осуществляет функция da taFile.println(); чтобы предотвратить добавление символа новой строки, вызывайте функцию dataFile.print(). Все данные направляются в буфер и записываются в файл только после выполнения команды dataFile.close().

Теперь напишем простую программу, которая создает на SD-карте файл log.csv и каждые 5 секунд записывает в него через запятую метки и какое-либо сообщение (листинг 13.3). В каждой строке файла CSV будет записана временная метка (текущее значение функции millis()) и некоторый текст. Программа может показаться вам бесполезной, но на самом деле это важный пример, подготавливающий взаимодействие с реальными датчиками, чем мы займемся в дальнейших проектах.

Листинг 13.3. Тест записи данных на SD-карту- write_to_sd.ino

// Запись данных на SD-карту

#include

// Подключение контактов

// MOSI = pin 11

// MISO = pin 12

// SCLK = pin 13

- 283 -

// Подключение контакта выбора CS

const int CS_PIN = 10;

// Контакт для питания SD-карты

const int POW_PIN =8;

void setup()

{

Serial.begin(9600);

Serial.println ("Initializing Card");

// Установить CS как выход

pinMode(CS_PIN, OUTPUT);

// Для питания карты используется вывод 8, установить HIGH

pinMode(POW_PIN, OUTPUT);

digitalWrite(POW_PIN, HIGH);

if ( !SD.begin(CS_PIN))

{

Serial.println("Card Failure");

return;

}

Serial.println("Card Ready");

}

void loop()

{

long timeStamp = millis();

String dataString = "Hello There!";

// Открыть файл и записать в него

File dataFile = SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.print(timeStamp);

dataFile.print(",");

dataFile.println(dataString);

dataFile.close(); // Данные не записаны,

// пока соединение не закрыто!

// Вывод в монитор для отладки

Serial.print(timeStamp);

Serial.print(",");

Serial.println(dataString);

}

else

{

Serial.println("Couldn't open log file");

}

delay(5000);

}

- 284 -

Обратите внимание на несколько моментов, особенно если у вас такой же переходник, как у меня (Cooking Hacks Micro SD shield):

• CS можно установить на любом контакте; если это не контакт по умолчанию (10), то необходимо в setup() предусмотреть команду pinMode(10, OUTPUT), иначе библиотека SD не будет работать;

• питание на переходник подается через контакт 8, поэтому POW_PIN должен быть установлен в качестве выхода и в setup() необходимо определить его значение как HIGH;

• при каждом проходе цикла loop() временная метка обновляется значением текущего времени, прошедшего с начала выполнения программы в миллисекундах. Переменная должна иметь тип long, потому что millis() возвращает число больше, чем 16 бит.

Файл открывается для записи и в него добавляются данные, разделенные запятыми.

Мы также выводим эти данные в последовательный порт для отладочных целей.

Если вы откроете терминал последовательного порта, то увидите вывод данных как на рис. 13.14.

Рис. 13.14. и Рис. 13.15.

При возникновении ошибок проверьте правильность подключения переходника, убедитесь, что SD-карта отформатирована и должным образом вставлена. Для проверки корректности записи данных вставьте SD-карту в компьютер и откройте файл в программе просмотра электронных таблиц (рис. 13.15). Обратите внимание,

- 285 -

что данные располагаются в таблице с учетом разделяющих запятых и символов перехода на новую строку.

13.2.4. Чтение с SD-карты

Теперь рассмотрим чтение информации с SD-карты. При регистрации данных это не нужно, но может оказаться полезным для установки параметров программы. Например, можно указать частоту регистрации данных.

Вставьте SD-карту в компьютер и создайте на ней новый текстовый файл с именем speed.txt. В этом файле просто введите время обновления в миллисекундах. На рис. 13.16 показано, что я задал время, равное 1000 мс ( 1 с).

После записи желаемой скорости обновления сохраните файл на SD-карте и вставьте карту в переходник, подключенный к Arduino. Теперь изменим програм

Рис. 13.16. Пример файла данных

- 286 -

му, предусмотрев чтение этого файла, извлечение информации и установку скорости обновления данных для регистрации.

Открыть файл для чтения позволяет та же команда so.open(), но без указания второго параметра FILE_WRITE. Поскольку класс File наследует свойства от класса потока (так же, как serial), можно использовать многие из полезных команд, например parseInt(). Сказанное иллюстрирует листинг 13.4.

Листинг 13.4. Пример чтения информации с SD-карты

File commandFile=SD.open("speed.txt");

if (commandFile)

{

Serial.println ( "Reading Command File");

while(commandFile.available() )

{

refresh_rate=commandFile.parseInt();

}

Serial.print("Refresh Rate = ");

Serial.print(refresh_rate);

Serial.println("ms");

}

else

{

Serial.println("Could not read command file.");

return;

}

Программа из листинга 13.4 открывает файл для чтения и считывает целые значения. Значение сохраняется в переменной частоты обновления, которую необходимо будет определить в начале программы. После чтения файл следует закрыть.

Теперь можно объединить листинги 13.3 и 13.4 и менять скорость записи, основываясь на содержимом файла speed.txt, как показано в листинге 13.5.

Листинг 13.5. Чтение и запись с SD-карты- sd_read_write.ino

// Чтение и запись SD-карты

#include

// Подключение контактов

//MOSI = pin 11

//MISO = pin 12

//SCLK = pin 13

// Подключение контакта выбора CS

const int CS_PIN =10;

const int POW_PIN =8;

// Скорость опроса по умолчанию

int refresh rate = 5000;

- 287 -

void setup()

{

Serial.begin(9600);

Serial.println("Initializing Card");

// Установить CS как выход

pinMode(CS_PIN, OUTPUT);

// Для питания карты используется контакт 8, установить HIGH

pinMode(POW_PIN, OUTPUT);

digitalWrite(POW_PIN, HIGH);

if ( !SD.begin(CS_PIN))

{

Serial.println("Card Failure");

return;

}

Serial.println("Card Ready");

// Чтение настроек (speed.txt)

File commandFile = SD.open("speed.txt");

if (comrnandFile)

{

Serial.println ( "Reading Comrnand File");

while(commandFile.available())

{

refresh_rate = commandFile.parseInt();

}

Serial.print("Refresh Rate = ");

Serial.print(refresh_rate);

Serial.println("ms");

commandFile.close(); // Закрыть файл настроек

}

else

{

Serial.println("Could not read command file.");

return;

}

}

void loop()

{

long timeStamp = millis();

String dataString = "Hello There!";

// Открыть файл и записать в него

File dataFile = SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.print(timeStamp);

dataFile.print(",");

dataFile.println(dataString);

dataFile.close(); // Закрыть файл

- 288 -

// Вывод в последовательный порт для отладки

Serial.print(timeStarnp);

Serial.print(",");

Serial.println(dataString);

}

else

{

Serial.println("Couldn't open log file");

}

delay(refresh_rate);

}

После загрузки на плату и запуска программы данные будут записываться с частотой, указанной при настройке. За процессом можно наблюдать в мониторе последовательного порта (рис. 13.17).

Рис. 13.17. Регистрация данных на скорости Refresh Rate, указанной в настройках

 

13.3. Использование часов реального времени

Почти каждое приложение регистрации данных выиграет от использования часов реального времени. Наличие часов реального времени (RТС) в системе позволит вставлять временные метки измерений, поэтому легко можно отследить, когда произошло событие. В предыдущем разделе мы вызывали функцию millis(), чтобы отследить время, прошедшее с начала включения платы Arduino. Теперь задейству

- 289 -

ем микросхему часов реального времени, позволяющую фиксировать текущее время регистрации данных на SD-карту.

13.3.1. Общие сведения о часах реального времени

Назначение часов реального времени ясно из названия. Вы устанавливаете время один раз, а часы продолжают очень точно отсчитывать время, даже с учетом високосных годов. В описанном далее примере выбрана популярная микросхема часов реального времени DS1307.

Микросхема часов реального времени DS1307

Часы реального времени DS1307 поддерживают связь с Arduino по протоколу I2C и подключаются к круглой батарейке, что обеспечивает ход часов в течение нескольких лет. К микросхеме подключается кварцевый резонатор, определяющий точность хронометража. Я выбрал плату расширения adafruit-DS1307-breakout (http://www.exploringarduino.com/parts/adafruit-DS1307-breakout), которая содержит микросхему DS1307, кварцевый резонатор, батарейку размером с монету, конденсатор развязки и I2C подтягивающие резисторы. Плату легко подключить к Arduino (рис. 13.18).

Рис. 13.18. Подключение платы расширения adafruit-DS1307-breakout к Arduino

Далее предполагается, что вы используете эту плату. Тем не менее, можно просто собрать схему из элементов на макетной плате и подключить непосредственно к Arduino. Потребуется кварцевый резонатор на 32,768 кГц, подтягивающие резисторы номиналом 2,2 кОм и круглая батарейка 3,0 В размером с монету. Если вы решили собрать плату самостоятельно, можете приобрести эти компоненты и собрать их на макетной плате по схеме, приведенной на рис. 13.19.

- 290 -

Рис. 13.19. Схема часов реального времени, собранная на макетной плате

Сторонняя библиотека Arduino RTClib

Как и в предыдущей главе, мы снова воспользуемся сторонней библиотекой для Arduino. На этот раз для облегчения связи с микросхемой часов реального времени (RTC). Библиотека называется RTClib, первоначально она была разработана JeeLabs, затем обновлена Adafruit Indusrtries. Ссылку для загрузки библиотеки можно найти на веб-странице http://www.exploringarduino.com/content/ch13. Скачайте библиотеку и распакуйте в папку libraries, как вы это делали в главе 12.

Работать с библиотекой просто. При первом выполнении кода нужно вызвать функцию RTC.adjust() для получения времени с компьютера и настройки часов.

Далее RTC работают автономно и можно получать текущее время и дату посредством команды RTC.now(). В следующем примере мы будем использовать эту функцию для ведения журнала регистрации в режиме реального времени.

13.3.2. Использование часов реального времени

Теперь объединим SD-карту и часы реального времени, чтобы включить ведение журнала с помощью временных меток. Мы модифицируем программу, добавив запись показаний часов реального времени вместо значений, выдаваемых функцией millis().

- 291 -

Подключение модулей SD card shield и RTC

Подключим к Arduino модули SD card shield и RTC. Если вы используете платы расширения Cooking Hacks Micro SD shield и adafruit-DS1307-breakout, то подключение будет выглядеть так, как на рис. 13.20.

Рис. 13.20. Плата Arduino с подключенными платами расширения Cooking Hacks Micro SD shield и adafruit-DS1307-breakout

Отметим, что последний контакт на RTC не связан с Arduino; это меандр, генерируемый RTC, в нашем примере он не задействован. В программе следует подать на контакт А2 уровень LOW и на A3 уровень HIGH (+5 В), чтобы обеспечить питание RTC. Если вы собрали свой модуль RTC на макетной плате, то установка будет выглядеть немного по-другому.

Модификация программы для работы с RTC

Теперь нужно добавить функционал RTC в нашу предыдущую программу. Необходимо сделать следующее:

• подключить библиотеку RTC;

• организовать питание модуля RTC (А2- LOW, A3-HIGH);

- 292 -

• инициализировать объект RTC;

• установить время RTC с помощью компьютера;

• записывать фактические временные метки в файл журнала.

Кроме того, в код программы добавлен вывод в файл заголовка столбца при каждом перезапуске журнала. Таким образом, вы легко найдете в журнале, записанном в файл CSV, моменты перезапуска.

ВНИМАНИЕ!

Если после запуска программы вы заметите, что она через некоторое время останавливается, то причина может заключаться в нехватке оперативной памяти. Так происходит из-за строк, которые занимают большой объем оперативной памяти, это относится к командам вывода в последовательный порт Serial.print() и Serial.println(). Проблему можно решить, удалив из программы указанные команды и поручив компилятору хранить строки не в оперативной памяти, а во флэшпамяти Arduino. Для этого для строк используем обертку F(), например Serial.println ( F ( "Hello") ). Описанный метод реализован в листинге 13.6.

Обновленная программа (листинг 13.6) использует RTC в качестве таймера для регистрации данных. Программа перемещает большинство строк во флэш-память, чтобы предотвратить переполнение оперативной памяти.

Листинг 13.6. Чтение и запись данных на SD-карту с использованием RTC - sd_read_write_rtc.ino

// Чтение и запись данных на SD-карту с использованием RTC

#include

// Подключение библиотеки SD

#include

// Для работы с RTC

#include "RTClib.h" // Подключение библиотеки RTC

// Подключение устройств SPI и I2C с контактами по умолчанию

// SD-карта SPI контакты

// RTC - стандартные I2C контакты

const int CS_PIN =10;

const int SD_POW_PIN =8;

const int RTC_POW_PIN =A3;

const int RTC_GND_PIN =А2.;

// Скорость опроса по умолчанию 5 секунд

int refresh rate = 5000;

// Создание объекта RTC

RTC_DS1307 RTC;

// Переменные для даты и времени

String year, month, day, hour, minute, second, time, date;

void setup()

{

Serial.begin(9600);

Serial.println(F("Initializing Card"));

// Настроить контакты CS и питания как выходы

pinMode(CS_PIN, OUTPUT);

- 293 -

pinMode(SD_POW_PIN, OUTPUT);

pinMode(RTC_POW_PIN, OUTPUT);

pinMode(RTC_GND_PIN, OUTPUT);

// Установка питания карты и RTC

digitalWrite(SD_POW_PIN, HIGH);

digitalWrite(RTC_POW_PIN, HIGH);

digitalWrite(RTC_GND_PIN, LOW);

// Инициализация Wire и RTC

Wire.begin();

RTC.begin();

// Если RTC не запущены, загрузить дату/время с компьютера

if ( ! RTC. isrunning())

{

Serial.println( F ( "RTC is NOT running ! ") );

RTC.adjust(DateTime(__DATE__, __TIME__ ));

}

// Инициализация SD-карты

if ( !SD.begin(CS_PIN))

{

Serial.println(F("Card Failure"));

return;

}

Serial.println(F("Card Ready"));

// Чтение конфигурационного файла (speed. txt)

File commandFile = SD.open("speed.txt");

if (commandFile)

{

Serial.println ( F ( "Reading Command File") );

while(commandFile.available())

{

refresh_rate = commandFile.parseInt();

}

Serial.print(F("Refresh Rate = "));

Serial.print(refresh_rate);

Serial.println(F("ms"));

commandFile.close();

}

else

{

Serial.println(F("Could not read command file."));

return;

}

// Запись заголовка

File dataFile = SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.println ( F ( "\nNew Log Started ! ") );

- 294 -

dataFile.println(F("Date,Time,Phrase"));

dataFile.close();

// Запись в последовательный порт

Serial.println(F("\nNew Log Started!"));

Serial.println(F("Date,Time,Phrase"));

}

else

{

Serial.println(F("Couldn't open log file"));

}

}

void loop()

{

// Получить значение даты и времени и перевести в строковые значения

DateTime datetime = RTC.now();

year = String(datetime.year(), DEC);

month = String(datetime.month(), DEC);

day = String(datetime.day(), DEC);

hour = String(datetime.hour(), DEC);

minute = String(datetime.minute(), DEC);

second = String(datetime.second(), DEC);

// Собрать строку текущей даты и времени

date = year + "/" + month + "/" + day;

time = hour + ":" + minute + ":" + second;

String dataString = "Hello There!";

// Открыть файл и записать значения

File dataFile = SD. open ( "log. csv", FILE_WRITE);

if (dataFile)

{

dataFile.print(date);

dataFile.print (F(", "));

dataFile.print(time);

dataFile.print(F(","));

dataFile.println(dataString);

dataFile.close();

// Вывод в последовательный порт для отладки

Serial.print(date);

Serial.print(F(","));

Serial.print(time);

Serial.print(F(","));

Serial.println(dataString);

}

else

{

Serial.println(F("Couldn't open log file"));

}

delay(refresh_rate);

}

- 295 -

Библиотека RTC импортируется в код строкой #include "RTClib.h" и создается объект RTC_DS1307 RTC. RTC является I2C-устройством, поэтому необходимо подключение библиотеки Wire, с которой мы знакомы из главы 8. В секции setup() функция RTC.isrunning() проверяет, запущена ли микросхема RTC. Если нет, то в микросхему записываются данные с часов компьютера, полученные при компиляции. После установки времени оно не сбрасывается, пока микросхема RTC подключена к батарее. В функции setup() в лог-файл записывается заголовок столбца, чтобы отслеживать моменты перезагрузки системы регистрации.

Во время цикла loop() инициализируем объект DataTime текущими значениями даты и времени из RTC. Из объекта DateTime извлекаем значения года, месяца, дня, часа, минуты, секунды, конвертируем их в строки и объединяем строки в общую строку для представления даты и времени. Эти данные записываются в лог-файл и выводятся в последовательный порт.

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

Рис. 13.21. Содержимое лог-файла в программе просмотра электронных таблиц

 

13.4. Регистратор прохода через дверь

Теперь можно приступить к созданию входного регистратора для вашей комнаты.

Отслеживать моменты прохода людей через дверь будем с помощью датчика расстояния. Регистратор будет фиксировать эти события и записывать в лог-файл на SD-карту для последующего просмотра на компьютере.

- 296 -

13.4.1. Схема регистратора

Все, что нужно сделать, - это добавить аналоговый датчик расстояния к существующей схеме. Если вы используете те же модули, как и я, не потребуется даже макетная плата. Просто подключите соответствующие контакты к земле, питанию и аналоговому входу A0. Монтажная схема приведена на рис. 13.22.

Рис. 13.22. Монтажная схема регистратора

Для того чтобы система работала стабильно, необходимо установить ИК-датчик расстояния так, чтобы луч датчика шел горизонтально вдоль всей двери. Тогда при проходе через дверь человек обязательно окажется в зоне действия датчика.

ИК-датчик расстояния и плату Arduino на время тестирования программы можно прикрепить к стене с помощью липкой ленты (рис. 13.23 ).

13.4.2. Программа для регистратора

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

- 297 -

Рис. 13.23. Крепление блоков регистратора возле входной двери показаний ИК-датчика расстояния и определение величины их изменения за время между опросами. Если изменение есть, можно предположить, что кто-то вошел или вышел из комнаты. Нужно выбрать порог изменения аналоговых показаний датчика для точного определения факта прохода через дверь. Я экспериментально определил, что для моей установки изменение значения аналогового сигнала более чем на 75 единиц является достаточным признаком для фиксации движения (у вас, возможно, будет по-другому). Чтобы надежно установить момент прохода, необходимо проверять датчик достаточно часто.

Я рекомендую записывать данные на SD-карту только в момент появления человека, а в остальное время обновлять журнал лишь с определенной периодичностью.

При этом будет обеспечен хороший баланс между объемом хранимой информации и точностью работы. Плата Arduino опрашивает датчик расстояния через 50 мс (и добавляет единицу к текущему столбцу каждый раз при обнаружении движения). Если движение не обнаруживается, записываем ноль в текущий столбец через 1 секунду (в отличие от 50 мс).

В листинге 13.7 приведена программа входного регистратора, работающая согласно описанному алгоритму.

Листинг 13.7. Программа входного регистратора - entrance_logger.ino

// Программа входного регистратора

#include

// Подключение библиотеки SD

#include

// Для работы с RTC

#include "RTClib.h" // Подключение библиотеки RTC

- 298 -

// Подключение устройств SPI и I2C с контактами по умолчанию

// SD-карта SPI контакты

// RTC - стандартные I2C контакты

const int CS PIN=10;

// SS для переходника SD

const int SD_POW_PIN =8;

// Питание Для SD

const int RTC_POW_PIN =A3; // Питание для платы RTC

const int RTC_GND_PIN =А2.; // Земля для платы RTC

const int IR_PIN=0; // ИК-датчик расстояния к аналоговому входу A0

// Создать объект RTC

RTC_DS1307 RTC;

// Инициализация переменных для даты/времени

String year, month, day, hour, minute, second, time, date;

// Инициализация переменных для ИК-датчика расстояния

int raw = 0;

int raw_prev = 0;

boolean active = false;

int update_time = 0;

void setup()

{

Serial.begin(9600);

Serial.println(F("Initializing Card"));

// Настроить контакты CS и питания как выходы

pinMode(CS_PIN, OUTPUT);

pinMode(SD_POW_PIN, OUTPUT);

pinMode(RTC_POW_PIN, OUTPUT);

pinMode(RTC_GND_PIN, OUTPUT);

// Установка питания карты и RTC

digitalWrite(SD_POW_PIN, HIGH);

digitalWrite(RTC_POW_PIN, HIGH);

digitalWrite(RTC_GND_PIN, LOW);

// Инициализация Wire и RTC

Wire.begin();

RTC.begin();

// Если RTC не запущены, загрузить дату/время с компьютера

if ( ! RTC. isrunning())

{

Serial.println(F("RTC is NOT running!"));

RTC.adjust(DateTime(__DATE__, __TIME__));

}

// Инициализация карты SD

if ( !SD.begin(CS_PIN))

{

Serial.println(F("Card Failure"));

return;

}

- 299 -

Serial.println(F("Card Ready"));

// Запись заголовка

File dataFile = SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.println(F("\nNew Log Started!"));

dataFile.println(F("Date,Time,Raw,Active"));

dataFile.close();

// Запись в последовательный порт для отладки

Serial.println(F("\nNew Log Started!"));

Serial.println(F("Date,Tirne,Raw,Active"));

}

else

{

Serial.println(F("Couldn't open log file"));

}

}

void loop()

{

// Получить значение даты и времени и перевести в строковые значения

DateTirne datetirne = RTC.now();

year = String(datetirne.year(), DEC);

rnonth = String(datetime.rnonth(), DEC);

day = String(datetirne.day(), DEC);

hour = String(datetirne.hour(), DEC);

rninute = String(datetime.rninute(), DEC);

second = String(datetime.second(), DEC);

// Собрать строку текущей даты и времени

date = year + "/" + rnonth + "/" + day;

time = hour + ":" + rninute + "·" + second;

// Собрать данные движения

raw = analogRead(IR_PIN);

// При изменении значения более чем на 75 между показаниями

// фиксируем факт прохода через дверь.

if (abs(raw-raw_prev) > 75)

active

true;

else

active=false;

raw_prev=raw;

// Открыть лог-файл и записать в него.

if (active || update_tirne == 20)

File dataFile=SD.open("log.csv", FILE_WRITE);

if (dataFile)

{

dataFile.print(date);

- 300 -

dataFile.print(F(","));

dataFile.print(time);

dataFile.print(F(","));

dataFile.print(raw);

dataFile.print(F(","));

dataFile.println(active);

dataFile.close();

// Вывод в последовательный порт для отладки

Serial.print(date);

Serial.print(F(","));

Serial.print(time);

Serial.print(F(","));

Serial.print(raw);

Serial.print(F(","));

Serial.println(active);

}

else

{

Serial.println(F("Couldn't open log file"));

}

update_time = 0;

}

delay(50);

update_time++;

}

13.4.3. Анализ зарегистрированных данных

После загрузки программы на плату Arduino установите регистратор возле двери и дайте поработать некоторое время. Когда наберется достаточное количество данных, вставьте SD-карту в компьютер и откройте лог-файл CSV в программе просмотра электронных таблиц. Предполагая, что на карте записаны данные для одного дня, можно построить график активности движения от времени. Пока никто не проходит через дверь, значение остается равным нулю. Когда кто-нибудь входит или выходит из комнаты, значение подскакивает до единицы, и можно точно узнать, когда это случилось. Процедура построения графика зависит от выбранного графического приложения. Я создал онлайн-таблицу, которая будет рисовать график. Чтобы воспользоваться этим сервисом, вам необходимо иметь учетную запись Google.

Посетите веб-страницу http://www.exploringarduino.com/content/ch13 и перейдите по ссылке на таблицу для построения графика. Вам будет предложено создать новую таблицу в своем аккаунте Google Drive. В итоге вы получите график данных, приведенный на рис. 13.24.

- 301 -

Рис. 13.24. Данные регистратора входа, представленные в графическом виде

Резюме

В этой главе вы узнали следующее:

• Что данные удобно хранить в CSV-файлах, использующих строки и запятые в качестве разделителей.

• Как отформатировать SD-карту памяти в операционных системах Windows, Mac и Linux.

• Что есть множество плат расширения SD-карт для Arduino, каждая со своими особенностями.

• Как использовать Arduino библиотеку SD для записи и чтения из файла на SD-карте.

• Как с помощью RTC вставить временные метки в данные регистратора.

• Как преодолеть ограничения оперативной памяти за счет хранения строк во флэш-памяти.

• Как обнаружить движение по изменению аналогового значения, полученного с ИК-датчика расстояния.

• Как построить на компьютере график данных от регистратора, используя программу просмотра электронных таблиц.

 

Глава 14. Подключение Arduino к Интернету

Список деталей

Для повторения примеров главы вам понадобятся следующие детали:

• плата Arduino (рекомендуется Uno);

• USB-кабель для программирования платы Arduino;

• плата расширения Ethemet shield;

• фоторезистор;

• датчик температуры ТМР36;

• RGB-светодиод;

• 1 резистор номиналом 10 кОм;

• 1 резистор номиналом 150 Ом;

• 3 резистора номиналом 220 Ом;

• динамик или пьезозуммер;

• кабель Ethemet;

• доступ к проводному маршрутизатору;

• набор перемычек;

• макетная плата.

Электронные ресурсы к главе

На странице http://www.exploringarduino.com/content/ch14 можно загрузить программный код, видеоуроки и другие материалы для данной главы. Кроме того, листинги примеров можно скачать со страницы www.wiley.com/go/exploringarduino в разделе Downloads.

Что вы узнаете в этой главе

Вот она, последняя глава. Запустим плату Arduino в виртуальное пространство, подключив ее к Интернету. Доступ к Интернету - очень сложная тема, можно написать целые тома книг о лучшем способе подключения Arduino к Интернету.

- 303 -

В этой главе мы рассмотрим использование платы расширения Arduino Ethemet shield для создания веб-страницы и отправки данных в Сеть. Вы узнаете о топологии Сети, о создании интернет-сайта, об использовании сторонних сервисов регистрации имен для подключения Arduino к Всемирной паутине.

 

14.1. Всемирная паутина, Arduino и Вы

Объяснить в одной главе, как работает Всемирная паутина, - слишком амбициозная затея, поэтому проиллюстрируем связь нашей платы Arduino с Интернетом с помощью упрощенной схемы, изображенной на рис. 14.1.

Рис. 14.1. Упрощенная схема взаимодействия Интернета и локальной сети

Если вы работаете только в локальной сети, то можете связаться с платой Arduino через веб-браузер компьютера, находящегося в той же локальной сети. Через маршрутизатор можно получить доступ к вашей плате Arduino из любой точки мира (или, по крайней мере, отовсюду, где есть доступ к Интернету).

14.1.1. Сетевые термины

Прежде чем подключить Arduino к сети, рассмотрим термины, которые встретятся нам на протяжении этой главы.

IP-адрес

IP-адрес представляет собой уникальный адрес, который идентифицирует каждое устройство, подключенное к сети. При работе в локальной сети есть на самом деле

- 304 -

два вида IP-адресов: IP-адреса внутренней сети и глобальный IP-адрес. Если в вашем доме или офисе установлен маршрутизатор ( см. рис. 14.1 ), то каждое устройство в пределах локальной сети обладает локальным IP-адресом, который виден всем устройствам вашей сети. Маршрутизатор/модем имеет один глобальный IP-адрес, который виден всему Интернету. Если вы хотите установить связь через Интернет между компьютером в другом месте и устройством, подключенным к маршрутизатору, необходимо преобразование сетевых адресов (NAT).

MAC-адрес

MAC-адреса, в отличие от IP-адресов, уникальны в пределах всей Сети (на практике они часто таковыми не являются). MAC-адреса назначаются каждому сетевому физическому устройству и не меняются. Например, когда вы покупаете компьютер, уникальные MAC-адреса присвоены и внутреннему модулю Wi-Fi, и адаптеру Ethernet. Это позволяет по MAC-адресу идентифицировать физические устройства в Сети.

HTML

HTML, или язык гипертекстовой разметки, является языком разметки документов в Интернете. Для отображения веб-страницы с вашего Arduino напишем простой HTML-документ, который создает кнопки и ползунки для отправки данных.

НТТР

НТТР, или протокол передачи гипертекста, определяет протокол для связи через World Wide Web (Всемирную паутину) и используется в браузерах. НТТР задает информацию заголовка, которая передается в виде части сообщения. Этот заголовок определяет, какая веб-страница будет отображаться и подтверждает успешное получение данных.

GET/POST

GET и POST определяют два способа передачи информации на удаленный вебсервер. Если вам встретится URL, который выглядит как www.jeremyblum.com/?s = arduino, то это запрос GET. GET определяет ряд переменных, следующих за вопросительным знаком в URL. В данном случае передается переменная s со значением arduino. Когда страница получает этот URL, он идентифицирует переменную s, выполняет поиск и возвращает страницу результатов. POST очень похож, но информация не видна непосредственно в URL. Вместо этого, данные передаются в фоновом режиме. POST обычно используется, чтобы скрыть конфиденциальную информацию.

DHCP

DHCP, или протокол динамической конфигурации узла, подключает устройства к локальной сети за один шаг. Чаще всего при подключении к Wi-Fi или проводной сети у вас на устройстве не установлен вручную IP-адрес, по которому маршру

- 305 -

тизатор может подключаться к вам. Как же маршрутизатор отправляет пакеты на сетевое устройство? При подключении к сети маршрутизатор инициирует запрос DHCP, что позволяет маршрутизатору динамически назначать вам доступный IP-адрес. В результате настройка сети оказывается гораздо проще, потому что не нужно заботиться о конфигурации сети для подключения к ней. Однако программа для Arduino усложняется, потому что следует выяснить, какой IP-адрес был присвоен.

DNS

Аббревиатура DNS расшифровывается как Domain Name System ( система домеых имен). Каждый веб-сайт, который расположен на сервере в сети Интернет, имеет уникальный IP-адрес. При вводе в www.google.com DNS-сервер смотрит на список, который информирует его о IP-адресе, связанном с этим URL. Затем он передает IP-адрес обратно в браузер вашего компьютера, чтобы браузер мог общаться с сервером Google. DNS позволяет вводить понятные имена вместо запоминания IP-адреса всех ваших любимых веб-сайтов. Можно образно сравнить DNS для вебсайтов со списком контактов в телефоне для телефонных номеров.

Клиенты и серверы

В этой главе вы узнаете, как использовать Arduino с платой расширения Ethernet shield в качестве сервера и клиента. Все устройства, подключенные к Интернету, выступают в роли сервера или клиента (хотя на самом деле часто исполняют обе роли). Сервер выдает информацию по запросу от запрашивающего компьютера, поступающему по сети. Это может быть запрос веб-страницы, данных из информационной базы, электронной почты и многого другого. Клиент - это устройство, которое запрашивает данные и получает ответ. При подключении компьютера к Интернету веб-браузер вашего компьютера выступает в качестве клиента.

Подключение к сети платы Arduino

Для всех примеров, приведенных в этой главе, необходимо использовать плату Arduino в паре с фирменным Ethernet-адаптером. Есть несколько версий этого адаптера, наши примеры протестированы на самой последней версии с контроллером WIZnet Ethernet. На более старых адаптерах установлен другой чип, что не гарантирует работу описанных примеров. Для экспериментов вы можете также взять плату Arduino Ethernet, которая содержит встроенный интерфейс Ethernet.

Совет Я рекомендую использовать внешний Ethernet-адаптер, т. к. он работает устойчивее, чем плата Arduino Ethernet со встроенным интерфейсом.

Прикрепите Ethernet-адаптер к вашей плате Arduino и подключите порт Ethernet адаптера к свободному порту Ethernet на вашем домашнем маршрутизаторе с помощью кабеля Ethernet. Подойдет обычный Ethernet-кабель типа "crossover". Соедините плату Arduino кабелем USB с вашим компьютером для программирования.

- 306 -

Если маршрутизатор находится далеко от компьютера, используемого для программирования, сначала запрограммируйте плату, а затем подключайте ее к маршрутизатору. Тем не менее, в некоторых примерах потребуется вывод информации, необходимой для отладки, в монитор последовательного порта. Чтобы не быть привязанным к компьютеру, можно подключить ЖК-дисплей для отображения IP-адреса, который в рассмотренных примерах выводится в последовательный терминал. Здесь пригодятся сведения из главы 10.

 

14.2. Управление платой Arduino из Интернета

Сначала используем плату Arduino в качестве веб-сервера. С помощью некоторых HTML-форм и интегрированной библиотеки Ethernet настроим Arduino для автоматического подключения к Сети и выведем веб-страницу, с помощью которой получим доступ для управления некоторыми контактами платы Arduino. Мы разместим кнопки на веб-интерфейсе для переключения цвета RGB-светодиода и управления частотой звука. Наша программа будет расширяемой, что позволит добавлять дополнительные устройства, чтобы более комфортно работать с Arduino.

14.2.1. Настройка оборудования для управления вводом-выводом

Настроим оборудование для управления платой Arduino в качестве сервера для управления контактами платы из сети Интернет. Для этого примера возьмем RGB-светодиод и пьезозуммер или обычный динамик. Схема соединения элементов показана на рис. 14.2. Напомним, что контакты 4, 10, 11, 12 и 13 предназначены для связи платы Arduino с адаптером Ethernet. Мы подключаем RGB-светодиод к выводам 5, 6, 7, а динамик - к выводу 3 платы Arduino.

14.2.2. Создание простой веб-страницы

Разработать веб-страницу можно независимо от Arduino. На нашей веб-странице будут простые кнопки для переключения цвета RGB-светодиода и ползунок для регулировки частоты звука, воспроизводимого динамиком. Веб-страница будет отправлять команды из браузера на сервер по протоколу НТТР GET. При разработке дизайна страницы веб-сайт может не быть подключенным к серверу Arduino, на этом этапе не нужно, чтобы взаимодействие со страницей вызывало какие-нибудь действия на плате Arduino.

Откройте свой любимый текстовый редактор (я рекомендую Notepad++ в Windows, который осуществляет визуальное цветовое выделение кода) и создайте новый файл с расширением html. Название не имеет значения, например test.html. Это будет пустая страница, без тегов, которые обычно присутствуют на веб-страницах, например и

. Но это не повлияет на отображение страницы в браузере. Поместите в HTML-файл код из листинга 14.1.

- 307 -

Рис. 14.2. Схема подключения к Arduino-серверу RGB-светодиода и динамика

Листинг 14.1. HTML-страница с формой - server_form.html

HTML-страница из листинга 14.1 содержит четыре элемента формы. Тег

задает начало формы, тег
- конец формы. Каждый элемент формы с тегом

- 308 -

определяет, какие данные будут переданы на сервер при отправке формы.

В нашем примере переменная называется L и на сервер передается номер необходимого вывода RGB-светодиода. Пустой элемент action указывает, что страница перезагружается при отправке формы. Значение скрытых полей (input с типом hidden) будут отправлены на сервер при нажатии кнопки submit. Для выбора частоты мы добавляем новый HTML-элемент (input с типом range). Это позволяет сделать ползунок диапазона. Вы можете перемещать ползунок с шагом 100 для выбора частоты, которая будет передаваться в переменной с именем s. Старые браузеры не поддерживают этот элемент ( слайдер) и он будет отображаться как поле ввода.

Чтобы увидеть, как будет выглядеть страница, запустите ваш браузер (я рекомендую Google Chrome ). В Chrome нужно нажать комбинацию клавиш + (в Windows) или + (в OS Х), чтобы вызвать диалоговое окно Открыть.

Откройте созданный HTML-файл (рис. 14.3).

Рис. 14.3. Вид нашей веб-страницы в браузере Chrome

При нажатии на любую из кнопок вы должны увидеть в адресной строке браузера переменные, передаваемые методом GET.

14.2.3. Написание программы для Arduino-сервера

Теперь нам необходимо созданный HTML-код веб-страницы интегрировать в программу Arduino-сервера, который будет обрабатывать подключение к сети, выдавать на запросы клиентов страницу и обрабатывать переменные, получаемые методом GET.

Подключение к сети и получение IP-адреса через DHCP

Благодаря механизму DHCP подключить к сети плату Arduino с Ethemet-адаптером очень просто. Перед обсуждением кода программы давайте посмотрим, как это будет происходить. Вначале нужно подключить библиотеки SPI и Ethemet, определить MAC-адрес вашего Ethemet-адаптера (он указан на этикетке) и создать экземпляр объекта EthernetServer. В функции setup() вы начинаете сеанс Ethernet с MAC-адреса, который вы определили, и запускаете веб-сервер. При необходимости можно назначить статический IP-адрес при инициировании Ethernet, но если этот аргумент пуст, то Arduino будет получать IP-адрес через DCHP и выведет назначенный IP-адрес в последовательный терминал. Затем этот IP-адрес потребуется для подключения к Arduino и просмотра веб-страницы.

- 309 -

Ответ на клиентский запрос

В основном цикле loop() происходит несколько действий. Для обработки различных действий проверяются переменные состояния, чтобы отслеживать, что было выполнено и что еще должно произойти для успешной связи с клиентом. Плата Arduino постоянно будет ожидать клиентских подключений к серверу, например с вашего ноутбука. При подключении клиента ответ содержит две вещи: НТТРзаголовок ответа и тип форматирования веб-страницы, выдаваемой по запросу. Заголовок ответа, отправляемый в браузер, сообщает, какая информация будет отправлена на запрос. Когда вы пытаетесь посетить несуществующую страницу, получаете "страшный" ответ: "404 Responce". Заголовок 404 указывает браузеру, что сервер не может найти запрашиваемую страницу. Ответ "200 Response" указывает, что запрос был получен и что HTML-данные будут передаваться в браузер.

Если вы хотите послать сообщение "200 Response" в браузер, а затем передать HTML-данные, то заголовок будет выглядеть так:

НТТР/1.1 200 ОК

Content-Type: text/html

После заголовка должно следовать содержимое вашей HTML-страницы. Программа также обрабатывает GET-запросы. Чтобы определить GET-запрос, нужно искать символ ? в URL. Если найден символ ?, программа ждет, пока не получит имя переменной. Команда для управления светодиодом - это L, команда для регулировки частоты динамика - s. Программа анализирует значения переменных и управляет сигналами на выводах Arduino. После этого следует команда break для выхода из цикла подключенного клиента, и программа начинает ожидать новое подключение клиента, чтобы повторить весь процесс сначала.

Итоговая программа веб-сервера

Теперь, учитывая все сказанное ранее, мы можем написать код программы вебсервера Arduino. Программа довольно нетривиальная, потому что содержит несколько переменных состояния, которые позволяют отслеживать взаимодействие между клиентом и сервером. Код, приведенный в листинге 14.2, управляет RGB-светодиодом и динамиком. В программу легко добавить функции с большим числом GET-переменных. Места, куда вы можете вставить дополнительную функциональность, указаны в комментариях листинга.

Листинг 14.2. Код веб-сервера - control_led_speaker.ino

// Arduino веб-сервер

// Часть адаптированного кода под лицензией MIT License от

// http://bildr.org/2011/06/arduino-ethernet-pin-control/

#include

#include

const int BLUE =5;

const int GREEN =6;

- 310 -

const int RED =7;

const int SPEAKER =3;

// Для управления RGB-светодиодом и динамиком

// Если вы хотите использовать дополнительные устройства,

// добавляйте переменные сюда

int freq = 0;

int pin;

// MAC-адрес устройства

// Должен быть написан на этикетке вашего устройства

// или можете использовать этот

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x4A, 0xE0 };

// Сервер на порту 80

EthernetServer server = EthernetServer(80); //Порт 80

boolean receiving = false; // Отслеживание данных GET

void setup()

{

Serial.begin(9600);

pinMode(RED, OUTPUT);

pinMode(GREEN, OUTPUT);

pinMode(BLUE, OUTPUT);

// Соединение с DHCP

if ( !Ethernet.begin (mac))

{

Serial.println("Could not Configure Ethernet with DHCP.");

return;

}

else

{

Serial.println("Ethernet Configured!");

}

// Запуск сервера

server.begin();

Serial.print("Server Started.\nLocal IP: ");

Serial.println(Ethernet.localIP());

}

void loop()

{

EthernetClient client

server.available();

if (client)

{

// Запрос НТТР заканчивается пустой строкой

boolean currentLineisBlank

true;

boolean sentHeader = false;

- 311 -

while (client.connected())

{

if (client.available())

{

char с = client.read(); // Чтение из буфера

if(receiving && с == ' ') receiving = false;

//Окончание передачи

if(c == '?') receiving = true; // Поиск символа?

// Ожидание данных GET

if(receiving)

// Данные управления светодиодом (переменная L)

if (с == 'L')

Serial.print ( "Toggling Pin ");

pin = client.parseInt();

Serial.println(pin);

digitalWrite(pin, !digitalRead(pin));

break;

}

// Команда управления динамиком (переменная S)

else if (с == 'S')

{

Serial.print("Setting Frequency to ");

freq = client.parseInt();

Serial.println(freq);

if (freq == 0)

noTone(SPEAKER);

else

tone(SPEAKER, freq);

break;

}

// Код для управления дополнительными устройствами

// добавляем сюда

}

// Отправка заголовка ответа

if ( ! sentHeader)

{

// Отправить стандартный заголовок НТТР ответа

client.println("HTTP/1.1 200 ОК");

client.println ( "Content-Type: text/html \n");

// Кнопка переключения красного для RGB

client.println ( "

");

client.println("");

client.println("");

client.println("

");

- 312 -

// кнопка переключения зеленого для RGB

client.println ( "

");

client.println("");

client.println("");

client.println("

");

// кнопка переключения синего для RGB

client.println ( "

");

client.println("");

client.println("");

client.println("

");

// Ползунок для выбора частоты динамика

client.println("

");

client.print("

"step = '100' value = '0'/>");

client.println("");

client.println("

");

// Добавить формы для управления

// дополнительными устройствами

sentHeader = true;

}

if (с=='\n' && currentLineisBlank) break;

if (с=='\n')

{

currentLineisBlank=true;

}

else if (с ! = '\r')

{

currentLineisBlank=false;

}

}

}

// Дать время веб-браузеру на получение данных

delay(5);

client.stop(); // Закрыть соединение

}

}

Приведенная программа выполняет все функции, описанные в предыдущих разделах. Измените MAC-адрес на значение, указанное на этикетке вашего Ethemet-адаптера. Если вы не найдете этот адрес, возьмите значение из листинга 14.2. Загрузите программу на плату Arduino и запустите монитор последовательного порта.

Убедитесь, что плата Arduino подключена к сети и в маршрутизаторе включен

DHCP. Через несколько секунд в мониторе последовательного порта появится сообщение о назначенном IP-адресе (рис. 14.4).

В случае, показанном на рис. 14.4, Arduino был назначен локальный IP-адрес 192.168.0.9. В вашей сети почти наверняка будет другой адрес. Зная IP-адрес, можно использовать его для доступа к веб-интерфейсу.

- 313 -

Рис. 14.4. Вывод в последовательный порт полученного по DHCP IP-адреса

 

14.3. Управление платой Arduino по сети

Теперь, когда код сервера работает, и ваша плата Arduino подключена к сети с действительным IP-адресом, вы можете с помощью браузера получить к ней доступ и осуществлять управление. Сначала сделаем это по локальной сети, а затем воспользуемся переадресацией портов на вашем роутере для доступа к плате Arduino из внешней сети.

14.3.1. Управление платой Arduino по локальной сети

Чтобы убедиться, что веб-интерфейс работает должным образом, проверьте, что ваш компьютер подключен к той же сети, что и плата Arduino (через Wi-Fi или Ethemet). Откройте ваш браузер и введите в адресную строку IP-адрес из предыдущего раздела. В результате должна открыться HTML-страница, созданная ранее.

Попробуйте нажать кнопки для включения/выключения цветов RGB-светодиода.

Переместите ползунок и нажмите кнопку установки частоты динамика. Плата Arduino должна отреагировать на ваши действия. Если открыть монитор последовательного порта Arduino IDE, вы увидите вывод в него отладочной информации.

Обратите внимание, что переменные передаются на сервер Arduino методом GET через адресную строку браузера (рис. 14.5).

Если управление платой Arduino по локальной сети проходит успешно, можно перейти к следующему разделу, чтобы управлять платой Arduino из любой точки мира.

- 314 -

Рис. 14.5. Управление платой Arduino через веб-интерфейс и вывод отладочной информации в последовательный порт

ПРИМЕЧАНИЕ

Видеоурок, демонстрирующий управление платой Arduino по локальной сети, можно посмотреть на странице http://www.exploringarduino.com/content/ch14. Этот видеофайл доступен также на сайте издательства Wiley.

14.3.2. Организация доступа к плате Arduino из внешней сети

В предыдущем разделе мы научились управлять платой Arduino из любой точки локальной сети, поскольку IP-адрес платы находился за маршрутизатором. Если требуется связать плату Arduino с компьютерами за пределами вашей локальной сети, следует воспользоваться передовыми технологиями, позволяющими получить доступ к устройству через ваш маршрутизатор из внешней сети.

- 315 -

Чтобы сделать это, нужно осуществить три действия:

1. Зарезервировать локальный адрес DHCP, используемый платой Arduino.

2. Перенаправить внешний порт на маршрутизаторе к внутреннему порту платы Arduino.

3. Подключить маршрутизатор к службе DNS.

ВНИМАНИЕ!

Последовательность действий зависит от типа маршрутизатора. Я буду опускать подробности, т. к. предполагаю наличие у вас некоторых знаний по администрированию маршрутизатора. Подробные указания для конкретного маршрутизатора можно отыскать в Интернете. Если это ваш первый опыт администрирования маршрутизатора, то можно нарушить настройки домашней сети. Некоторые маршрутизаторы даже не поддерживают все функции, необходимые для включения переадресации портов и динамического обновления DNS. Если вы совсем не знакомы с сетевым администрированием, ограничьтесь доступом к плате Arduino из локальной сети.

Вход в панель администрирования маршрутизатора

Сначала необходимо войти в панель администрирования маршрутизатора. URL-адресом панели администрирования является IP-адрес шлюза для вашей сети. Почти во всех конфигурациях домашних сетей это первые три значения локального IP-адреса платы Arduino, разделенные точками и дополненные единицей. Если, например, IP-адрес вашей платы Arduino равен 192.168.0.9, то ваш адрес шлюза, скорее всего (но не обязательно), будет 192.168.0.1. Попробуйте ввести этот адрес в адресную строку браузера, чтобы увидеть окно входа в систему. Введите учетные данные для входа к странице администрирования маршрутизатора ( это не данные для беспроводного соединения). Если вы никогда не меняли значения, заданные по умолчанию, то можете найти их в руководстве по настройке маршрутизатора.

Если этот IP-адрес не работает, придется назначить его вручную. В Windows можно вызвать командную строку и ввести в нее команду ipconfig. Используйте адреса шлюза по умолчанию для активного сетевого подключения. В ОС Mac выберите в меню Системные настройки пункт Сеть, нажмите кнопку Дополнительно, перейдите на вкладку TCP/IP и используйте значение адреса маршрутизатора. Если вы находитесь в Linux, откройте терминал, наберите route -n и задайте в качестве адреса маршрутизатора последний Gateway, отличный от нуля.

Резервирование IP-адреса для Arduino в DHCP

После того как вы окажетесь в консоли администратора маршрутизатора, необходимо зарезервировать адрес DHCP. При этом гарантируется, что каждый раз при подключении к маршрутизатору устройства с определенным MAC-адресом ему будет присвоен одинаковый локальный IP-адрес. При резервировании IP-адреса DHCP никогда не отдаст этот адрес клиентам с MAC-адресом, отличным от указанного, даже если клиент с зарезервированным адресом в настоящее время не подключен к маршрутизатору. Резервируя в DHCP IP-адрес платы Arduino, вы всегда будете иметь возможность направить трафик к нему на следующем шаге.

- 316 -

Как только вы найдете эту опцию, зарезервируйте текущий IP-адрес платы Arduino, назначив ее MAC-адрес, установленный в предыдущей программе. Для применения настройки необходима перезагрузка маршрутизатора. Убедитесь в правильности настройки, перезагрузив маршрутизатор и посмотрев, что Arduino получает тот же IP-адрес после восстановления.

ПРИМЕЧАНИЕ

Аналогичного эффекта можно добиться, назначив плате Arduino статический IP (не используя DHCP) в программе. Узнать, как это сделать, вы можете на странице

http://arduino.cc/en/Reference/EthernetlPAddress.

Перенаправление порта 80 на плату Arduino

Теперь, когда у вас есть неизменный локальный IP-адрес для платы Arduino, необходимо перенаправить входящий интернет-трафик на внутренний IP-адрес. Port

Forwarding- это прослушивание трафика на определенном порту маршрутизатора и перенаправление его на определенный внутренний IP-адрес. Порт 80 по умолчанию предназначен для НТТР-связи. Найдите пункт Port Forwarding в панели администрирования маршрутизатора и назначьте перенаправление внешнего порта 80 на внутренний порт 80, на IP-адрес вашей платы Arduino. Если маршрутизатор определяет диапазон для портов, просто выберите опцию 80-80. Теперь весь трафик к маршрутизатору через порт 80 будет идти на вашу плату Arduino.

Обновление динамического DNS

Последний шаг - получение доступа к маршрутизатору из сети Интернет. Возможно, у вас задан статический глобальный IP-адрес. Это бывает довольно редко для домашних интернет-соединений, но все же иногда встречается. Если вы знаете статический IP, то можете получить доступ к этому IP-адресу из любой точки мира и направить трафик на плату Arduino. Можно даже приобрести доменное имя и настроить серверы DNS на ваш IP-адрес.

Но может оказаться, что у вас динамический глобальный IP-адрес. Провайдер, скорее всего, меняет ваш IP-адрес один раз в несколько дней или недель. Таким образом, даже если удастся выяснить ваш глобальный IP-адрес на сегодняшний день и получить доступ к Arduino через этот IP-адрес, он может перестать работать завтра.

Способ решения этой проблемы - использование динамических служб IP. Эти службы запускают небольшую программу на маршрутизаторе, которая периодически проверяет ваш глобальный IP-адрес и сообщает его на удаленный веб-сервер.

Веб-сервер обновляет ваш поддомен (например, myarduino.dyndns.org), и вы всегда попадаете на глобальный IP-адрес маршрутизатора, даже если он изменяется.

DynDNS - это сервис, программное обеспечение которого встроено в большинство современных маршрутизаторов. Поищите на странице администрирования маршрутизатора, какой сервис DynDNS он поддерживает. Некоторые из них бесплатны, некоторые взимают ежегодную плату. Вы можете следовать инструкциям по установке в панели администрирования маршрутизатора, чтобы создать учетную запись с одной из этих служб и подключить его к маршрутизатору. В итоге вы сможете получить доступ к Arduino удаленно, даже с динамически меняющимся

- 317 -

глобальным IP-адресом. Если ваш маршрутизатор не поддерживает услуги DynDNS, имейте в виду, что некоторые сервисы предлагают услуги DynDNS клиентам, которые будут работать на компьютерах в вашей сети, а не на маршрутизаторе.

Как только будет определен ваш публичный IP-адрес (или динамически обновляемый URL ), его можно указать в вашем браузере и подключение к Arduino должно заработать. Дайте этот адрес другу, чтобы проверить функционирование удаленно.

 

14.4. Отправка данных в реальном времени в графические сервисы

В предыдущем разделе вы узнали, как превратить плату Arduino в веб-сервер, который предоставляет интерфейс для управления контактами платы по локальной сети или через Интернет. Существует еще одна распространенная причина для подключения Arduino к Интернету - создание сети датчиков. Поскольку датчики обычно только передают информацию, а не принимают команды, Arduino будет инициализировать запрос к известному ресурсу в Интернете (через онлайн-сервис графиков) и вам не придется заботиться о переадресации IP-адресов, резервировании IP-адреса и т. п.

В этом разделе используется веб-ресурс Xively (ранее Cosm) для создания графиков данных, получаемых с платы Arduino.

14.4.1. Создание потока данных на Xively

В рассматриваемом примере мы с помощью веб-сервиса Xively через Интернет будем строить графики данных, получаемых платой Arduino от датчиков. При подключении к сайту Xively вы будете освобождены от большей части нетворческой работы, которую приходится делать для отображения данных в Интернете.

Создание учетной записи Xively

Для начала, посетите страницу http://www.xively.com и создайте бесплатный аккаунт. Перейдите по ссылке, содержащейся в электронном письме с подтверждением, и вы сможете войти на сайт.

Создание потока данных

Как только аккаунт будет создан, нажмите кнопку Develop в верхней части страницы, чтобы создать канал. Затем нажмите кнопку Add Device. В окне (рис. 14.6) вам будет предложено назвать свой канал и добавить его описание. Вы также можете сделать ваш канал публичным или приватным.

После ввода необходимых данных нажмите кнопку Add Device. Появится страница с сообщением о созданном новом потоке. Оставьте страницу открытой, т. к.

эта информация пригодится нам в дальнейшем при настройке программы для Arduino.

- 318 -

Рис. 14.6. Добавление устройства в Xively

Установка библиотек Xively и HTTPClient

Xively предоставляет для Arduino удобную библиотеку, облегчающую доступ через Интернет. Библиотеки Xively и HttpClient связаны друг с другом, поэтому их необходимо скачать с сайта GitHub. Посетите следующие две ссылки:

https://github.com/xively/xively-Arduino и https://github.com/amcewen/HttpClient и нажмите кнопку Download ZIP для скачивания архивов. Ссылки для скачивания библиотек можно найти на веб-странице http://www.exploringarduino.com/content/ch14.

Скачав файлы, выполните следующие действия:

1. Распакуйте файлы и переименуйте папки библиотек так, чтобы они не содержали символа тире (GitHub добавляет тире к именам папок автоматически). Я рекомендую переименовать папку HttpClient-master в HttpClient, а папку XivelyArduino-master в xively.

2. Переместите эти папки в каталог библиотек Arduino, как было описано в главе 12.

3. Откройте Arduino IDE (если программа была открыта при копировании библиотек, необходима перезагрузка компьютера) и перейдите в меню Файл -> При

- 319 -

меры. В списке должны появиться папки HttpClient и xively. Значит, библиотеки были успешно установлены.

Для первого эксперимента с Xively рассмотрим пример публикации в Сети состояния одного аналогового датчика. В Arduino IDE откроем пример DatastreamUpload из папки xively. Так как мы будем модифицировать код примера, создадим новый проект, используя в меню Файл опцию Сохранить как. В примере идет передача аналогового значения датчика, подсоединенного к контакту 2:

// Analog pin which we're monitoring (0 and 1 are used Ьу the

// Ethernet shield)

int sensorPin = 2;

В следующем разделе подключим аналоговый датчик к плате Arduino с установленным адаптером Ethemet.

Подключение к плате Arduino

Подсоединим датчик к аналоговому контакту 2 платы Arduino. Пример иллюстрирует чтение аналогового входа 2 и отправку данных в ваш аккаунт на Xively. Возьмем фоторезистор и резистор номиналом 10 кОм и подключим их к аналоговому входу 2 в качестве делителя напряжения (рис. 14.7), как вы это делали в главе 3.

Затем подключим плату Arduino к компьютеру и к Сети.

Рис. 14.7. Подключение фоторезистора к плате Arduino с установленным адаптером Ethernet

- 320 -

Настройка Xively и выполнение программы

Вы уже установили соответствующие библиотеки и открыли проект с примером.

Его нужно настроить, скомпилировать и запустить на вашей плате Arduino. Сначала настроим программу в соответствии с параметрами устройства в нашем аккаунте Xively. Вам нужно изменить значение трех параметров: MAC-адрес вашего адаптера Ethemet, Xively API key и Feed ID. MAC-адрес будет таким же, как и в предыдущих примерах. xively API key и Feed ID можно найти в вашем аккаунте Xively в разделе API Keys (рис. 14.8).

Рис. 14.8. Параметры Xi veli API key и Feed ID

В этом разделе вы найдете Feed ID (первый параметр) и Xively API key (второй параметр) для вставки в код программы. Далее приведены строки, которые нужно обновить соответствующими значениями.

Замените MAC-адрес:

// MAC-адрес вашего адаптера Ethernet

byte mac[] = { 0xDE, 0xAD, 0xВЕ, 0xEF, 0xFE, 0xED };

Замените Xively API Кеу своим значением:

// Ваш Xively API key

char xivelyKey[] = "YOUR_XIVELY_API_KEY";

Замените Feed ID (15552 в примере) своим значением:

// Feed ID

XivelyFeed feed(15552, datastreams, 1 /* номер потока */);

В листинге 14.3 приведен полный код программы.

Листинг 14.3. Загрузка потока данных в Xively - xively.ino

#include

#include

#include

#include

// MAC-адрес Ethernet-адаптера

byte mac[] = { 0x90, 0xА2, 0xDA, 0x00, 0x4A, 0xE0 };

- 321 -

// Ваш Xively АР! key

char xivelyKey[] = "qkjXSloUKqbCG-hqh3fw4WisdvOSAKx4ZXZYSWhGUWdxcz0g";

// Аналоговый контакт для подключения датчика

int sensorPin = 2;

// Строка для идентификаторов потока

char sensorId[] = "sensor_reading";

XivelyDatastrearn datastrearns[] = {

XivelyDatastrearn(sensorId, strlen(sensorId), DATASTREAM_FLOAT),

};

// Feed ID

XivelyFeed feed(1242622121, datastrearns, 1 /* number of datastrearns */);

EthernetClient client;

XivelyClient xivelyclient(client);

void setup()

{

// Запуск последовательного порта

Serial.begin(9600);

Serial.println("Starting single datastrearn upload to Xively... ");

Serial.println();

while (Ethernet.begin(rnac) != 1)

{

Serial.println("Error getting IP address via DHCP, trying again... ");

delay(15000);

}

}

void loop()

{

int sensorValue = analogRead(sensorPin);

datastrearns[OJ.setFloat(sensorValue);

Serial.print("Read sensor value ");

Serial.println(datastrearns[O).getFloat());

Serial.println("Uploading it to Xively");

int ret = xivelyclient.put(feed, xivelyKey);

Serial.print("xivelyclient.put returned ");

Serial.println(ret);

Serial.println();

delay(15000);

}

Загрузите программу в плату Arduino, и все будет готово к передаче данных. При первом подключении Arduino сервер Xively автоматически добавляет выдачу данных на веб-странице, открытой вами ранее.

В программе вы создаете объект, который содержит всю информацию о вашем потоке. Данные представлены в виде массива datastrearns [], содержащего имя дат

- 322 -

чика и тип значений (в данном случае float). Объект XivelyFeed содержит идентификатор потока, информацию о потоке данных и количестве потоков данных, которые находятся в массиве.

Отображение данных на веб-странице

Данные будут передаваться сразу после загрузки программы в плату Arduino. Если вы не видите возвращенный статус "200" в мониторе последовательного порта, то, вероятно, ошиблись при копировании параметров Xively API key или Feed ID. Проверьте эти значения и повторите попытку. Если все работает, вернитесь на сайт Xively, поток данных sensor_reading теперь должен автоматически обновляться каждые 15 с. Нажмите на ссылку sensor_reading, и вы увидите график значений, поступающих от фоторезистора. Через некоторое время график может выглядеть примерно так, как показано на рис. 14.9. Вот и все, что нужно сделать. Плата Arduino будет продолжать отправлять данные и обновлять информацию на сервере

Рис. 14.9. Отображение значений фоторезистора на сайте Xively

14.4.2. Добавление компонентов в поток

Один датчик, отображающий поток в Xively, это здорово, но может вы хотите добавить больше датчиков? К счастью, это сделать очень легко. Помимо фоторезистора, установим на плате Arduino датчик температуры. Можно добавить любой другой датчик, включая цифровые датчики I2C и SPI.

- 323 -

Добавление аналогового датчика температуры добавим простой аналоговый датчик температуры ТМР36 (см. главу 3), подсоединив его к аналоговому контакту 3, как показано на рис. 14.10.

Рис. 14.10. Добавление аналогового датчика температуры ТМР36

Добавление показаний датчика в поток

Теперь нужно вставить данные от нового датчика в поток данных, отправляемых на сервер Xively. По сути, в коде программы нужно просто прибавить дополнительный поток данных везде, где указан первый поток данных. Вы также можете переименовать потоки с идентификаторами datastream на более понятные, например light_reading и temp_reading. Код листинга 14.4 похож на предыдущий, но теперь у нас два потока данных. Параметры Xively API key, Feed ID и MAC-адрес такие же, как в предыдущей программе.

Листинг 14.4. Загрузка потока данных в Xively от нескоьких датчиков - xively2.ino

#include

#include

#include

#include

- 324 -

// MAC-адрес вашего адаптера Ethernet

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x4A, 0xE0 };

// Your Xively key to let you upload data

char xivelyKey[] = "qkjXSloUKqbCG-hqh3fw4WisdvOSAKx4ZXZYSWhGUWdxcz0g";

// Аналоговые датчики подключения устройств (0 и 1 уже задействованы

// адаптером Ethernet)

int lightPin = 2; // Датчик температуры

int tempPin = 3; // Фоторезистор

// Строки для идентификаторов потока

char lightid[] = "light_reading";

char tempid[] = "temp_reading";

XivelyDatastream datastreams[] = {

XivelyDatastream(lightid, strlen(lightid), DATASTREAM_FLOAT),

XivelyDatastream(tempid, strlen(tempid), DATASTREAM_FLOAT),

};

// Feed ID

XivelyFeed feed(1242622121, datastreams, 2 /* number of datastreams */);

EthernetClient client;

XivelyClient xivelyclient(client);

void setup()

{

// Запуск последовательного порта

Serial.begin(9600);

Serial.println("Starting double datastream upload to Xively... ");

Serial.println();

while (Ethernet.begin(mac) != 1)

{

Serial.println("Error getting IP address via DHCP, trying again... ");

delay(15000);

}

}

void loop()

{

int lightValue = analogRead(lightPin);

datastreams[O].setFloat(lightValue);

Serial.print("Read light value ");

Serial.println(datastreams[O].getFloat());

int tempValue = analogRead(tempPin);

datastreams[1]Доступный мне файл PDF, очевидно, изготовлен сканированием русского перевода книги. В свою очередь, рисунки для этого перевода сканировались из английского источника. В результате — качество двух (или более) сканирований — ужасно. См. рис. 14.2.
.setFloat(tempValue);

Serial.print("Read temp value ");

Serial.println(datastreams[1]Доступный мне файл PDF, очевидно, изготовлен сканированием русского перевода книги. В свою очередь, рисунки для этого перевода сканировались из английского источника. В результате — качество двух (или более) сканирований — ужасно. См. рис. 14.2.
.getFloat());

Serial.println("Uploading it to Xively");

- 325 -

int ret = xivelyclient.put(feed, xivelyKey);

Serial.print("xivelyclient.put returned ");

Serial.println(ret);

Serial.println();

delay(15000);

}

Прежде всего, заметим, что предыдущие ссылки на датчик (фоторезистор) были обновлены. Теперь, когда информация поступает от двух датчиков, их необходимо различать. Поток данных tempid[] добавлен в объект datastreams[]. Определение объекта xivelyFeed было обновлено, чтобы показать, что у нас не один, а два потока данных. В цикле loop() строки кода вывода данных фоторезистора продублированы для датчика температуры. Обратите внимание, что поток для фоторезистора представлен в объекте datastreams первым, и на него ссылка задана как datastreams [0]. Поток для датчика температуры в объекте datastreams представлен вторым, и ссылка на него datastreams [1]Доступный мне файл PDF, очевидно, изготовлен сканированием русского перевода книги. В свою очередь, рисунки для этого перевода сканировались из английского источника. В результате — качество двух (или более) сканирований — ужасно. См. рис. 14.2.
. При запуске программы на плате Arduino веб-интерфейс автоматически обновляет отображение потоков данных. Возможно, вы захотите удалить старый поток данных sensor_reading, т. к. он заменен потоком light reading. После нескольких минут работы Arduino графики должны выглядеть примерно так, как показано на рис. 14.11.

Рис. 14.11. Отображение на сайте Xively данных от нескольких датчиков

Итак, из платы Arduino вы успешно создали как веб-сервер, так и клиент удаленной веб-службы. Попробуйте теперь добавить цифровые датчики и визуальную обратную связь, чтобы сделать вашу систему по-настоящему интерактивной.

- 326 -

Резюме

В этой главе вы узнали следующее:

• Каков смысл параметров IP, DHCP, DNS, MAC и др.

• В чем состоят различия между клиентами и серверами.

• Как создать HTML-код для формы управления платой Arduino через Интернет.

• Как запустить веб-сервер на вашей плате Arduino.

• Как управлять контактами ввода-вывода на Arduino через Интернет.

• Как подключить Arduino к серверу Xively для построения графиков.

• Как отображать онлайн-данные, поступающие от нескольких датчиков.Основа всех плат Arduino - микроконтроллеры Atmel. Здесь мы не будем рассматривать особенности микроконтроллеров для всех плат Arduino, но приведем технический паспорт контроллера ATmega, чтобы получить представление, как он работает. Кроме того, мы опишем принципиальную схему платы Arduino Uno, чтобы лучше понять, как она функционирует.

Знакомство с технической документацией

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

Анализ технического описания

Рассмотрим, например, техническое описание для микроконтроллера ATmega 328Р.

Напомним, что микросхема ATmega 328 используется в Arduino Uno и во многих других платах. Найти технический паспорт бывает нелегко. Я рекомендую ввести в Google запрос "ATmega 328Р datasheet" и выбрать из перечня ссылок PDF-файл на сайте Atmel. Техническое описание микроконтроллеров, применяемых в Arduino, можно найти на сайте http://www.arduino.cc в разделах технических описаний плат. Начнем с рассмотрения первой страницы технического паспорта (рис. П.1 ).

В большинстве случае, на первой странице говорится об особенностях данного микроконтроллера.

Даже беглый взгляд на первую страницу дает много информации о микроконтроллере. Мы видим, что микроконтроллер имеет 32 Кбайт программируемой флэшпамяти, может быть перепрограммирован примерно 10 000 раз, напряжение питания составляет от 1,8 до 5,5 В (в случае Arduino 5 В). Здесь также можно найти сведения о числе входов-выходов, поддержке специальных интерфейсов (SPI, I2C), разрядности аналого-цифрового преобразователя (АЦП).

- 328 -

Рис. П.1. Первая страница технического описания микроконтроллера ATmega 328Р Документация на микроконтроллер ATmega и схема платы Arduino

- 329 -

На самом деле техническое описание насчитывает сотни страниц, я хочу отметить только наиболее важные темы, на которые следует обратить внимание. Техническое описание, как правило, снабжено PDF-закладками, обеспечивающими быстрый поиск нужной информации.

Особый интерес представляет информация о портах ввода-вывода, таймерах, различных аппаратных интерфейсах. В качестве примера рассмотрим рисунок 13-1 из раздела I/O-Ports технического описания, который приведен здесь на рис. П.2. Схемы, подобные этой, присутствуют во всем техническом описании и позволяют глубже понять принцип действия платы Arduino. На приведенной схеме видно, что все контакты ввода-вывода снабжены защитными диодами, предотвращающими появление на выводе отрицательного напряжения. Важную роль играет и конденсатор Cpin, определяющий интервалы времени фронта и спада.

Рис. П.2. Фрагмент технического описания ATmega 328Р: схема контакта ввода-вывода

Цоколевка микросхемы ATmega 328Р

Все технические описания содержат цоколевку, которая четко иллюстрирует функции каждого вывода. Выводы микроконтроллеров могут выполнять несколько функций, поэтому изучение цоколевки помогает уяснить назначение того или иного контакта. Рассмотрим цоколевку ATmega 328Р (рис. П.3). Изучив цоколевку микроконтроллера ATmega, легче будет понять схему Arduino Uno, которую мы рассмотрим в следующем разделе.

Обратите внимание, что расположение выводов на рис. П.3 и на самом микроконтроллере идентично. Полукруг (метка) в верхней части соответствует аналогичному полукругу на корпусе микросхемы и позволяет определить местоположение вывода 1.

Возможно, вы заметили аббревиатуры, не встречавшиеся ранее. Рассмотрим некоторые из них:

• VCC - источник питания микросхемы (в случае Arduino 5 В);

• AVCC - отдельное напряжение питания для АЦП (в случае Arduino 5 В);

- 330 -

Рис. П.3. Цоколевка микроконтроллера ATmega 328Р

Рис. П.4. Схема подключения выводов ATmega 328p к пронумерованным контактам Arduino Документация на микроконтроллер ATmega и схема ппаты Arduino

- 331 -

Остальные выводы контроллера - вводы-выводы общего назначения. Каждый из них сопоставляется с уникальным номером контакта в программном обеспечении Arduino, так что вам не придется помнить букву и номер порта. Названия в скобках представляют собой запись альтернативных функций для каждого вывода. Например, выводы PDO и PD 1 также являются контактами универсального синхронно-асинхронного приемопередатчика Rx и Тх соответственно. Выводы РВ6 и РВ7 служат для подключения внешнего кварцевого резонатора. На плате Arduino Uno к ним подключен кварцевый резонатор 16 МГц и вы не можете их использовать в качестве контактов ввода-вывода. Если у вас возникли проблемы с расшифровкой названий, дополнительную информацию можно отыскать в тех разделах технического описания, где приведена терминология. На сайте Arduino в разделе http://arduino.cc/en/Hacking/PinMapping168 можно найти схему подключения выводов ATmega к пронумерованным контактам платы Arduino (рис. П.4).

Принципиальная схема Arduino

Один из лучших способов изучения проектирования - анализ схем существующих продуктов, например, Arduino. На рис. П.5 изображена принципиальная схема платы Arduino Uno.

Можете ли вы, глядя на рис. П.5, установить соответствие элементов схемы и реальной платы Arduino? Начните с микроконтроллера (MCU) (элемент ZU4 на схеме) и проследите, как связаны выводы ATmega 328p с контактами разъема. Нетрудно убедиться, что выводы ATmega соответствуют контактам, доступным в интегрированной среде разработки (IDE) Arduino. Ранее мы отмечали, что выводы PDO и PD 1 подключены к контактам USART Rx и Тх. По схеме Arduino можно увидеть, что эти выводы подключены к соответствующим контактам контроллера 16U2 (конвертер USB в последовательный порт). Вы также знаете, что к контакту 13 Arduino подключен светодиод. Из схемы видно, что контакт 13 подключен к выводу PBS на ATmega. Но где же светодиод? Обозначив провода специальными метками, можно указать связь между ними в разных частях схемы без соединения линиями. На первый взгляд это может показаться непонятным. Внимательно посмотрев на вывод PBS, можно заметить, что провод, идущий из MCU, помечен как SCK, а в верхней части схемы есть провод, тоже помеченный SCK и подключенный к светодиоду. Такие обозначения соединительных проводов встречаются на большинстве принципиальных схем. Уяснив общий принцип, продолжайте анализировать схему Arduino, пока не поймете назначение всех соединений.

Рис. П.5. Схема платы Arduino Uno RevЗ

************************************************