Text
                    Виктор Петин
Проекты
с
контроллера ДГ[|ШП0
4-е издание
Санкт-Петербург
« БХВ-Петербург»
2021


УДК 004 ББК 32.973.26 П29 Петин В. А. П29 Проекты с использованием контроллера Arduino. — 4-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2021. — 560 с: ил. — (Электроника) ISBN 978-5-9775-6711-4 Рассмотрены основные платы Arduino и платы расширения (шилды), добавляющие функциональность основной плате. Подробно описан язык и среда программирования Arduino IDE. Приведены практические проекты с использованием контроллеров семейства Arduino в области робототехники, погодных метеостанций, "умного дома", вендинга, телевидения, беспроводной связи (bluetooth, радиоуправление, связь с устройствами Android) и др. Все проекты сопровождаются схемами и листингами. На сайте издательства размещен архив с исходными кодами программ и библиотек, описаниями и спецификациями электронных компонентов. В четвертом издании рассмотрены новые платы Arduino MKR и Nano 33, новые платы расширения, светодиодные матрицы, протокол MQTT, RFID-идентифика- ция, GPS-трекер, ЯндексКарты и проекты в области нейронных сетей. Для читателей, интересующихся современной электроникой УДК 004 ББК 32.973.26 Группа подготовки издания: Руководитель проекта Евгений Рыбаков Зав. редакцией Екатерина Сависте Компьютерная верстка Ольги Сергиенко Дизайн обложки Марины Дамбиевой Оформление обложки Карины Соловьевой Подписано в печать 11.01.21. Формат 70хЮ01/1в. Печать офсетная. Усл. печ. л. 45,15. Тираж 1500 экз. Заказ № 007. "БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20. Отпечатано с готового оригинал-макета ООО "Принт-М", 142300, М.О., г. Чехов, ул. Полиграфистов, д. 1 ISBN 978-5-9775-6711-4 © ооо тав", 2021 ©Оформление ООО "БХВ-Петербург", 2021
Оглавление ••••••••••••••• XXDv/ДИЗЛОВИv •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••*••••••••••••• Для кого и о чем эта книга? 13 Структура книги 13 Благодарности 14 ЧАСТЬ I. ARDUINO — ОБЩИЙ ОБЗОР 15 1.1. Arduino — что это? 17 1.2. В чем преимущество Arduino? * 18 1.3. Новые тенденции и перспективы развития Arduino 18 Глава 2. Платы семейства Arduino и платы расширения для них..................... 20 2.1. Обзор плат семейства Arduino 20 2.2. Arduino Uno 21 2.3. Arduino Nano 22 2.4. Arduino Pro Mini 23 2.5. Arduino LilyPad 24 2.6. Arduino Mega2560 25 2.7. Arduino Leonardo 26 2.8. Arduino Due 27 2.9. Arduino Yun 28 2.10. Arduino MKR WiFi 1010 29 2.11. Arduino Nano 33 30 2.12. Платы расширения для Arduino 31 ЧАСТЬ П. СРЕДЫ РАЗРАБОТКИ И ЯЗЫК ПРОГРАММИРОВАНИЯ Ш1АТ ARDUINO 33 Глава 3. Среда разработки Arduino IDE 35 3.1. Установка Arduino IDE в Windows 35 3.2. Установка Arduino IDE в Linux 36 3.3. Настройка среды Arduino IDE 37
Оглавление Глава 4. Облачная среда разработки Arduino Create 41 4.1. Начало работы в среде Arduino Create , 41 4.2. Загрузка и выполнение пробного скетча 43 Глава 5. Программирование плат Arduino 45 5.1. Базовые знания 45 5.1.1. Цифровые выводы 45 5.1.2. Аналоговые входы 46 5.1.3. Широтно-импульсная модуляция 46 5.1.4. Память в Arduino 46 5.2. Струюура программы 48 5.2.1. Функции setupO и loopO 48 5.3. Синтаксис и операторы .....49 5.3.1. Управляющие операторы 49 5.3.1.1. Оператор //(условие) и операторы сравнения ==, /=, < , > 49 5.3.1.2. Оператор //.. .else 49 5.3.1.3. Оператор/or 50 5.3.1.4. Оператор switch 51 5.3.1.5. Оператор while 51 5.3.1.6. Оператор do...while 52 5.3.1.7. Оператор break 52 5.3.1.8. Оператор continue 52 5.3.1.9. Оператор return 53 5.3.2. Синтаксис 53 5.3.2.1.; (semicolon, точка с запятой) , 53 5.3.2.2. {} (curly braces, фигурные скобки) 53 5.3.2.3. Комментарии: //(single line comment, однострочный), /* */ (multi-line comment, многострочный) 54 5.3.3. Арифметические операторы 54 5.3.3.1. = (assignment, оператор присваивания) 54 5.3.3.2. + (сложение), - (вычитание), * (умножение), /(деление) 55 5.3.3.3.% (modulo) 55 5.3.4. Операторы сравнения 55 5.3.5. Логические операторы 55 5.3.5.1. && (логическое И) 55 5.3.5.2. || (логическое ИЛИ) 55 5.3.5.3. / (логическое отрицание) 56 5.3.6. Унарные операторы 56 5.3.6.1. ++ (увеличение значения), /—(уменьшение значения) 56 5.3.6.2. +=,-=, *= ,/= 56 5.4. Данные 56 5.4.1. Типы данных 56 5.4. 5.4. 5.4. 5.4. 5.4. 5.4. .1. boolean 56 .2. char '. 57 3.byte 4 57 A.int : 57 .5. unsigned int 58 .6. long 58
Оглавление 5.4.1.7. unsigned long 58 5.4.1.8. float 59 5.4.1.9. double 59 5.4.1.10. string — текстовые строки 59 5.4.1.11. Массивы ..60 5.4.1.12. void. 61 5.4.2. Константы 61 5.4.3. Переменные 62 5.4.3.1. Объявление переменных 62 5.4.3.2. Границы переменных 62 5.4.4. Преобразование типов данных 63 SAA.l.charO 63 5.4.4.2. byteO 63 5.4.4.3. intO 63 5.4.4.4. longO 63 5AA.5.floatO .'. 63 5.5. Функции 64 5.5.1. Цифровой ввод/вывод '. 64 5.5.1.1. ФункциярinMode 64 5.5.1.2. Функция digitalWriteO 64 5.5.1.3. Функция digitalReadO 65 5.5.2. Аналоговый ввод/вывод 66 5.5.2.1. Функция analogReadQ 66 5.5.2.2. Функция analogReferenceO 67 5.5.2.3. Функция analogWriteO 67 5.5.3. Дополнительные фунции ввода/вывода 68 5.5.3.1. Функция tone() 68 5.5.3.2. Функция поТопеО 69 5.5.3.3. Функция shiftOutO , 69 5.5.3.4. Функцияpulseln() 71 5.5.4. Работа со временем 72 5.5.4.1. Функция millisO 72 5.5.4.2. Функция microsO 72 5.5.4.3. Функция delayO 73 5.5.4.4. Функция delayMicrosecondsQ 74 5.5.5. Математические функции 74 5.5.5.1. Функция min(x,yx) 75 5.5.5.2. Функция max(x, у) 75 5.5.5.3. Функция absO 75 5.5.5.4. Функция constraint, a, b) 76 5.5.5.5. Футащя mapfvalue, fromLow, fromHigh, toLow, toHigh) 76 5.5.5.6. Функцияpow(basef exponent) 77 5.5.5.7. Функция sq(x) 77 5.5.5 8. Функция sqrt(x) 77 5.5.6. Тригонометрические функции 77 5.5.6.1. Функщя sin(rad) 78 5.5.6.2. Функция cos(rad) 78 5.5.6.3. Функция tan(rad) 78
Оглавление 5.5.7. Генераторы случайных значений 78 5.5.7.1. Функция randomSeed(seed) 78 5.5.7.2. Функция randomO 78 5.5.8. Операции с битами и байтами 79 5.5.8.1. Функция lowByteO 79 5.5.8.2. Функция highByteO 80 5.5.8.3. Функция bitReadO 80 5.5.8.4. Функция bitWriteO 80 5.5.8.5. Функция bitSetO 80 5.5.8.6. Функция bitClearO 81 5.5.8.7. Функция Ыф 81 5.5.9. Внешние прерывания 81 5.5.9.1. Функция attachlnterrupt 81 5.5.9.2. Функция detqchlnterrupt 82 5.6. Управление портами через регистры ATmega 83 ЧАСТЬ Ш. ПРАКТИЧЕСКОЕ ПРИМЕНЕНИЕ ARDUINO 85 Глава 6. Проекты для изучения выводов Arduino 87 6.1. Цифровые выводы — «бегущий огонь» на светодиодах 87 6.1.1. Подключение светодиода к выводу Arduino 88 6.1.2. Подключение к плате Arduino 8 светодиодов 92 6.2. Цифровые входы — управляем светодиодами с помощью кнопок 97 6.2.1. Подключение кнопки к плате Arduino 97 6.2.2. Управление кнопками количеством горящих светодиодов 104 6.3. Аналоговые входы — светодиодный индикатор аналоговых значений 107 6.3.1. Подключение потенциометра к плате Arduino 109 6.3.2. Вывод показаний потенциометра на светодиодную шкалу 112 6.4. ШИМ — радуга на RGB-светодиоде 114 6.4.1. Подключение к плате Arduino RGB-светодиода 115 6.5. Светодиодные индикаторы 118 6.5.1. Подключение к плате Arduino семисегментного индикатора 118 6.6. Расширение цифровых выходов — микросхема 74НС595 122 6.6.1. Подключение к плате Arduino сдвигового регистра 74НС595 123 6.7. Расширение цифровых входов и выходов — микросхема МСР23017 128 6.8. Расширение аналоговых входов — мультиплексор CD4051 129 Глава 7. Использование библиотек в проектах Arduino 132 7.1. Установка библиотек 132 7.1.1. Установка библиотеки через Менеджер библиотек 133 7.1.2. Установка библиотеки из ZIP-архива 133 7.1.3. Установка библиотеки вручную 135 7.2. Подключение библиотеки 135 7.3. Создание собственной библиотеки 136 7.3.1. Создание заголовочного файла D5651.h 136 7.3.2. Создание файла реализации D5651.cpp 137 7.3.3. Создание файла keywords.txt 138
Оглавление Глава 8. Arduino и последовательный порт UART 140 8.1. Библиотека Serial 140 8.1.1. Функция Serial.begin 140 8.1.2. Функция Serialprint 141 8.1.3. Функция Serialprintln 141 8.1.4. Функция Serialwrite 141 8.1.5. Функция Serial.available 142 8.1.6. Функция Serial.read 142 8.2. Использование UART для отладки программ 142 8.2.1. Подключение к плате Arduino нескольких кнопок 142 8.3. Использование UART для установки параметров 145 8.4. Библиотека SoftwareSerial * 149 8.5. Соединение по UART двух плат Arduino 150 Глава 9. Подключение датчиков к плате Arduino 153 9.1. Подключение аналоговых датчиков 153 9.1.1. Подключение к плате Arduino аналогового датчика температуры LM335 154 9.2. Подключение датчиков по протоколу 1-Wire 156 9.2.1. Подключение к плате Arduino цифрового датчика температуры DS18B20 157 9.3. Подключение датчиков влажности и температуры DHT 163 9.4. Подключение датчиков по протоколу 12С 167 9.4.1. Подключение к плате Arduino датчика интенсивности света ВН1750 169 Глава 10. Использование дисплеев в проектах Arduino...................................... 173 10.1. Символьные дисплеи на микроконтроллере HD44780 173 10.1.1. Функция beginO 176 10.1.2. Функция plearQ 176 10. 10. 10. 10. 10. .3. Функция homeO 177 .4. Функция setCursorQ .....177 .5. Функция writeQ 177 .6. Функция printO 177 .7. Функция cursorQ 178 10.1.8. Функция noCursorO 178 10.1.9. Функция W/иЭД 178 10.1.10. Функция noBlinkO 178 10.1.11. Функция displayO 17$ 10.1.12. Функция noDisplayO 179 10.1.13. Функция scrollDisplayLeftO 179 10.1.14. Функция scrollDisplayRightQ 179 10. 10. 10. 10. 10. .15. Функция autoscrollO 179 .16. Функция noAutoscrollQ 180 Al^yHKumleftToRightO 180 .18. Функция rightToLeftO 180 .19. Функция createCharQ 180 10.2. Подключение дисплеев на контроллере HD44780 по протоколу 12С 181 10.2.1. Вывод на ЖК-дисплей данных с датчика, работающего по протоколу 12С 183 10.3. Графический дисплей Nokia 186 10.4. OLED-дисплеи 192 10.4.1. Электронные часы на OLED-дисплее 193
8 Оглавление 10.5. Дисплеи Nextion 197 10.5.1. Создание нового проекта для дисплея Nextion 198 10.5.2. Прошивка дисплея через UART 202 10.5.3. Прошивка дисплея с помощью карты microSD 203 10.5.4. Подключение дисплея Nextion к плате Arduino 203 10.6. Светодиодные матрицы 207 10.6.1. Четырехразрядная светодиодная матрица 207 10.6.2. Вывод на четырехразрядную светодиодную матрицу спрайтов и символов 210 10.6.3. Бегущая строка на четырехразрядной светодиодной матрице 213 10.6.4. Русификация «бегущей строки» на четырехразрядной светодиодной матрице 215 10.6.5. Матрица 16x16 на светодиодах WS2812 218 10.6.6. Arduino-библиотека Adafruit Neopixel 220 10.6.7. Графический аудиоспектроанализатор на матрице 16*16 светодиодов\У82812 221 Глава 11. Подключение к Arduino исполнительных устройств .225 11.1. Подключение к плате Arduino электромагнитного или твердотельного реле 225 11.2. Подключение к плате Arduino электродвигателя постоянного тока 229 11.2.1. Управление двигателем с помощью транзистора 229 11.3. Управление двигателями с помощью драйвера 231 11.4. Подключение к плате Arduino сервопривода 234 11.4.1. Использование сервопривода в проекте звуковой сигнализации 237 11.5. Подключение к плате Arduino шагового двигателя 239 11.5.1. Управление дроблением шага и направлением вращения шагового двигателя с платы Arduino 242 11.6. Подключение к плате Arduino бесколлекторного двигателя 245 Глава 12. Arduino и беспроводная связь 248 12.1.ИК-управление 248 12.1.1. Управление сервоприводом с помощью ИК-связи 251 12.2. Радиомодули для частоты 433 МГц 254 12.2.1. Управление светодиодом платы Arduino с другой такой же платы по радиоканалу 433 МГц 254 12.3. Радиомодули NRF24L01 257 12.3.1. Организация связи между двумя платами Arduino с использованием модулей NRF24L01 259 12.4. Использование Arduino с аппаратурой радиоуправления 264 12.4.1. Принципы формирования радиосигнала 266 12.4.2. Организация связи приемника с передатчиком 267 12.4.3. Разработка скетча для приема платой Arduino команд передатчика 268 12.5. Arduino и Bluetooth 271 Глава 13. Arduino и Интернет вещей 279 13.1. Подключение к Интернету с помощью платы расширения Ethernet shield 279 13.1.1. Получение IP-адреса по DHCP 280 13.1.2. Отправка данных на сайт «Народный мониторинг» через Ethernet shield 282 13.2. Подключение к Интернету с помощью платы расширения GSM/GPRS shield 289 13.2.1. Отправка и получение SMS-сообщений с помощью GSM/GPRS shield 291 13.2.2. Отправка данных на сайт «Народный мониторинг» через GSM/GPRS shield 295
Оглавление 9 13.3. Протокол MQTT , 299 13.3.1. Отправка данных по протоколу MQTT 300 13.3.2. Получение данных по протоколу MQTT 304 13.3.3. Android-приложение IoT MQTT Dashboard ._. 307 13.4. Плата Arduino MKR WiFi 1010 для проектов IoT 308 13.5. Отправка данных в облако Arduino IoT Cloud и получение их оттуда 312 Глава 14. RFED-идентификация 318 14.1. Считыватель RFIDRC522 , 318 14.2. Организация контроля доступа по RFID-меткам 321 14.3. Запись информации на RFID-метку 323 14.4. Проект «Говорящая фотография» 330 Глава 15. Специальные возможности отдельных плат Arduino 334 15.1. Использование Arduino Leonardo в качестве USB-устройства 334 15.1.1. Arduino Leonardo: имитация клавиатуры 335 15.1.2. Блокируем клавиатуру с наступлением темноты 337 15.1.3. Arduino Leonardo: имитация компьютерной мыши 337 15.2. Плата Arduino Esplora 340 15.2.1. Arduino Esplora: установка цветов RGB-светодиода 343 15.2.2. Arduino Esplora: создание игры 344 15.3. Плата Arduino LilyPad 362 15.4. Плата Arduino Yun 367 15.4.1. Плата расширения Arduino Yun shield 367 15.4.2. Arduino Yun shield: управляем веб-камерой 369 Глава 16. Взаимодействие Arduino с другими программируемыми системами 372 16.1. Использование Arduino в проектах LEGO 372 16.1.1. Получение микрокомпьютером LEGO данных с датчика влажности и температуры DHT11, подключенного к плате Arduino 373 16.2. Arduino в проектах ROS 376 16.2.1. Установка ROS \ 377 16.2.2. Узлы и темы в ROS 377 16.2.3. Пакет rosserial 378 16.2.4. Подготовка сообщения (publisher) на Arduino 379 16.2.5. Создание подписки (subscriber) на Arduino 383 16.2.6. Связь через ROS двух плат Arduino 385 16.3. Arduino и Raspberry pi 386 16.3.1. Установка WeblOPi на Raspberry Pi 387 16.3.2. Обмен данными по последовательному порту 389 16.3.3. Управление движущейся платформой на базе Arduino по web-интерфейсу Raspberry Pi 390 Глава 17. Программирование в среде Arduino IDE других плат ••...••..•••••.••••••..398 17.1.ESP8266 — микроконтроллер с интерфейсом Wi-Fi 398 17.1 Л. Установка Arduino IDE для работы с ESP8266 399 17.1.2. Печать курса валют на термопринтере в проекте Интернета вещей 402
10 Оглавление П.2. Z-Uno — плата для прототипирования устройств Z-Wave 409 17.2.1. Установка Arduino IDE для Z-Uno 411 17.2.2. Подключение к плате Z-Uno датчика влажности DHT11 414 ЧАСТЬ IV. ИНТЕРЕСНЫЕ ПРОЕКТЫ НА ARDUINO 417 Глава 18* Умная теплица 419 18.1. Мониторинг климатических параметров умной теплицы 420 18.2. Индикация показаний умной теплицы 425 18.3. Организация полива, обдува и освещения в умной теплице 431 18.4. Переносим функции мониторинга и управления теплицей на устройство с ОС Android 440 18.5. Создаем собственное мобильное приложение для управления умной теплицей 447 18.6. Превращаем нашу умную теплицу в объект Интернета вещей 451 Глава 19. GPS-трекер и онлайн-сервис поиска стоянок . 461 19.1. Подключение GPS-модуля к плате Arduino '. 461 1?.2. Отправка данных по GPRS на сервер 464 19.3. Создание веб-страницы с использованием API Яндекс.Карт 472 Глава 20. Проекты для вендинга: купюроприемник, монетоприемник, разменный автомат 476 20.1. Купюроприемник ICT серий А7 и V7 476 20.1.1. Подключение купюроприемника ICT V7 к плате Arduino 480 20.1.2. Скетч для получения номинала принимаемой купюры 482 20.2. Монетоприемник СН-926 483 20.2.1. Настройка монетоприемника 484 20.2.2. Калибровка монетоприемника 485 20.3. Разменный автомат (хоппер) Cube Hopper MKII 485 20.3.1. Подключение хоппера к плате Arduino 487 20.3.2. Программирование хоппера 487 Глава 21. Создание управляющей платы для автомойки самообслуживания 492 21.1. Блок приема денежных средств и блок индикации t 492 21.2. Выбор программ работы мойки 496 21.3. Отсчет времени выполнения программы. Реализация паузы 499 21.4. Режим администратора. Установка параметров 501 Глава 22. Arduino и интерфейс USB: управление роботами 503 22.1. Интерфейс USB 503 22.2. Плата расширения USB Host Shield 504 22.3. HID-устройства USB 505 22.4. Подключение HID-мыши USB 508 22.5. Управление роботом с помощью руля Defender 508 22.6. Управление роботом с помощью геймпада Defender 518
Оглавление tf. Глава 23. Камера Pixy: реализация компьютерного зрения •.•.••••••.••.•••••••••••••.••525 23.1. Настройка камеры 526 23.2. Подключение камеры Pixy к плате Arduino 527 23.3. Организация слежения камерой за объектом 529 Глава 24. Проекты на плате Nano 33 BLE Sense ....................................532 24.1. Щчало работы с платой Nano 33 BLE Sense 533 24.2. Bluetooth Low Energy (BLE) 536 24.3. Отправка данных с датчиков платы Nano 33 BLE Sense no BLE 537 24.4. БиблиотекаТеп8огР1о>у Lite 540 24.5. Пример создания классификатора объектов с обучением 541 24.5.1. Сбор данных „ 542 24.5.2. Обучение модели 544 24.5.3. Скетч классификатора для запуска нейронной сети на плате Nano 33 BLE Sense 549 •••••••••••••••••••••••••••• Приложение 1. Перечень использованных источи и ков...................................... 555 Приложение 2. Описание электронного архива .....................556 Предметный указатель ..............................................................................................557
Предисловие Для кого и о чем эта книга? Книга ориентирована на читателей, желающих быстро освоить темы программирования плат Arduino и использования их для связи с внешними системами в проектах автоматизации и робототехники. В книге содержится описание языка программирования плат Arduino в среде Arduino IDE и предлагается изучение основ работы с платами Arduino на множестве реальных примеров и проектов их использования, имеющих практическое значение и представляющих собой законченные решения, пригодные для включения в ваши конструкции. Все проекты содержат электрические и (или) монтажные схемы соединений и листинги соответствующих программ. Все задействованные в проектах книги детали и компоненты нетрудно приобрести в специализированных магазинах, кроме того, для удобства читателей на сайте издательства «БХВ-Петербург» (https://bhv.ru/product-category/nabory-dlya-mejkerov/) предлагаются наборы деталей и компонентов, с помощью которых вы сможете быстро адаптироваться в мире Arduino и получить удовольствие, видя, как оживают ваши творения! Книга сопровождается электронным архивом, содержащим исходный код всех рассмотренных примеров и проектов, используемые в проектах необходимые библиотеки, а также техническую документацию на задействованные в проектах устройства и датчики (см. приложение 2). Этот электронный архив можно скачать с FTP- сервера издательства «БХВ-Петербург» по ссылке ftp://ftp.bhv.ru/9785977567114.zip, а также со страницы книги на сайте www.bhv.ru. Структура книги Книга состоит из четырех частей и включает предисловие, двадцать четыре главы и два приложения. ? Часть I содержит описание платформы Arduino, обзор плат семейства Arduino и плат расширения для Arduino.
14 Предисловие ? В части II книги рассмотрены среда разработки Arduino IDE, новая облачная среда разработки Arduino Create и язык программирования для плат Arduino. D Часть III посвящена изучению возможностей платы Arduino, использованию в связке с ней датчиков, дисплеев, исполнительных устройств, организации беспроводного соединения плат Arduino, созданию устройств Интернета вещей. Рассмотрено также взаимодействие Arduino с другими системами и программирование в среде Arduino ШЕ других плат. О Часть IV посвящена созданию конкретных устройств на основе платы Arduino. ? В приложениях приведены перечень использованных источников и описание электронного архива, сопровождающего книгу. Благодарности Хочу поблагодарить родных и близких, которые с пониманием относились к потраченному на книгу (за счет общения с ними) времени. Особая благодарность Богаче- вой Марине Николаевне за поддержку и понимание. Большая благодарность издательству «БХВ-Петербург», где поверили в необходимость этой книги, и всем сотрудникам издательства, которые помогали мне в ее создании. Благодарю также всех читателей, купивших эту книгу, — надеюсь, она поможет вам в разработке собственных проектов на основе Arduino.
ЧАСТЬ I О0 Arduino — общий обзор Глава 1. Введение в Arduino Глава 2. Платы семейства Arduino и платы расширения для них
ГЛАВА 1 Введение в Arduino 1.1. Arduino — что это? Появление первых микроконтроллеров ознаменовало начало новой эры в развитии микропроцессорной техники. Сосредоточение в одном корпусе большинства системных устройств сделало микроконтроллер подобным обычному компьютеру. В отечественной литературе они даже назывались однокристальными микроЭВМ. Соответственно и желание использовать микроконтроллеры как обычные компьютеры возникло практически одновременно .с их появлением. Но желание это сдерживалось многими факторами. Например, чтобы собрать устройство на микроконтроллере, необходимо знать основы схемотехники, устройство и работу конкретного процессора, уметь программировать на ассемблере и изготавливать электронную технику. Потребуются также программаторы, отладчики и другие вспомогательные устройства. В итоге без огромного объема знаний и дорогостоящего оборудования не обойтись. Такая ситуация долго не позволяла многим любителям использовать микроконтроллеры в своих проектах. Сейчас, с появлением устройств, дающих возможность работать с микроконтроллерами без наличия серьезной материальной базы и знания многих предметов, все изменилось. Примером такого устройства может служить проект Arduino итальянских разработчиков. Arduino и его клоны представляют собой наборы, состоящие из готового электронного блока и программного обеспечения. Электронный блок здесь — это печатная плата с установленным микроконтроллером и минимумом элементов, необходимых для его работы. Фактически электронный блок Arduino является аналогом материнской платы современного компьютера — на нем имеются разъемы для подключения внешних устройств, а также разъем для связи с компьютером, по которому и осуществляется программирование микроконтроллера. Особенности используемых микроконтроллеров ATmega фирмы Atmel позволяют производить программирование без применения специальных программаторов. Все, что нужно для создания нового электронного устройства, — это плата Arduino, кабель связи и компьютер. Второй частью проекта Arduino является программное обеспечение для создания управляющих программ. Оно объединило в себе простейшую среду разработки и язык программирования, представляющий собой вариант языка C/C++ для микро-
Часть I. Arduino — общий обзор контроллеров. При этом в него добавлены элементы, позволяющие создавать программы без изучения аппаратной части. Так что для работы с Arduino практически достаточно знания только основ программирования на C/C++. Создано для Arduino и множество библиотек, содержащих код, работающий с различными устройствами. 1.2. В чем преимущество Arduino? Пользователь современного компьютера не задумывается о функционировании отдельных частей ПК. Он просто запускает нужные программы и работает с ними. Точно так же и Arduino позволяет пользователю сосредоточиться на разработке проектов, а не на изучении устройства и принципов функционирования отдельных элементов. Нет надобности и в создании законченных плат и модулей — разработчик может использовать готовые платы расширения или просто напрямую подключить к Arduino необходимые элементы. Все остальные усилия будут направлены на разработку и отладку управляющей программы на языке высокого уровня. В итоге доступ к разработке микропроцессорных устройств получили не только профессионалы, но и просто любители что-то сделать своими руками. Наличие готовых модулей и библиотек программ позволяет непрофессионалам в электронике создавать готовые работающие устройства для решения своих задач. А варианты использования Arduino ограничены только возможностями микроконтроллера и имеющейся платы, ну и, конечно, фантазией разработчика. 1.3. Новые тенденции и перспективы развития Arduino Классический форм-фактор Arduino является знаковым, но он постепенно отмирает. Uno-подобные платы, которые стали стандартом де-факто в мире любителей радиоэлектроники, медленно уходят на пенсию. В настоящее время внимание Arduino в большей степени направлено на Интернет вещей (IoT). Сначала появилась серия плат MKR, предоставляющая различные варианты подключения к сети и управления питанием, что побуждает людей использовать их в качестве единого стандартного формата для проектов IoT. Платы MKR повысили уровень стандартизации процесса проектирования, облегчая жизнь разработчика, пытающегося продать свой конечный продукт на рынке. Затем появилась серия Nano 33. Ее платы имеют практически те же размеры, что и оригинал Nano, но несут на борту новые процессоры и отличаются низким энергопотреблением. Это и плата Nano Every для обычных проектов на базе мощного микроконтроллера Microchip ATmega4809 с микросхемой ATSAMD11 ARM Cortex-MO и процессором для USB-to-serial связи, и Nano IoT на микросхеме ATSAMD21 ARM Cortex-MO с поддержкой Wi-Fi и Bluetooth LE и с крипточипом для безопасного хранения сертификатов и общих ключей, совместимая с новым облаком Arduino IoT, и Nano 33 BLE — энергоэффективные модули для работы по технологии Bluetooth BLE.
Глава 1. Введение в Arduino 19_ Эти новые Nano-платы могут быть совместимы с макетом, но больше предназначены для поверхностного монтажа в виде модулей. Они на самом деле нацелены на мелких производителей, которые изготовили прототипы на классических или MKR-платах и хотят довести свой продукт до полномасштабного производства. На выставке CES 2020 компания Arduino представила новое семейство мощных устройств Arduino Portenta, разработанное для требовательных промышленных приложений, обработки задач искусственного интеллекта и для робототехники. Оно оборудовано широким спектром поддерживаемых соединений для подключения периферийных устройств.
ГЛАВА 2 Платы семейства Arduino и платы расширения для них 2.1. Обзор плат семейства Arduino Основные версии плат Arduino представлены следующими моделями: ? Due — плата на базе 32-битного ARM микропроцессора Atmel SAM3X8E ARM Cortex-M3; ? Leonardo — плата на микроконтроллере ATmega32U4; ? Uno — самая популярная версия базовой платформы Arduino; ? Duemilanove — плата на микроконтроллере ATmegal68 или ATmega328; ? Diecimila — версия базовой платформы Arduino USB; ? Nano — компактная плата, используемая как макет. Она подключается к компьютеру при помощи кабеля USB Mini-B; ? Mega — версия серии плат Mega на базе микроконтроллера ATmegal280; ? Mega2560— плата на базе микроконтроллера ATmega2560 с использованием микроконтроллера ATMega8U2 для последовательного соединения по USB- порту; ? Mega ADK— версия платы Mega2560 с поддержкой интерфейса USB-host для связи со смартфонами на Android и другими устройствами с интерфейсом USB; ? Arduino ВТ — плата с модулем Bluetooth для беспроводной связи и программирования; ? LilyPad— плата, разработанная для носимых устройств (может зашиваться в ткань); ? Fio— плата, разработанная для беспроводных применений. Содержит разъем для радио ХВее, разъем для батареи Li-Po и встроенную схему подзарядки; ? Mini — самая маленькая плата Arduino; ? Pro — плата, разработанная для опытных пользователей (может являться частью большего проекта);
Глава 2. Платы семейства Arduino и платы расширения для них 2? ? Pro Mini — как и плата Pro, разработана для опытных пользователей, которым требуется низкая цена, меньшие размеры и дополнительная функциональность; , ? Arduino Yun — плата на основе Arduino Leonardo, объединяющая в себе достоинства двух платформ, поддерживаемых Свободным сообществом: Arduino и Linux. Получившийся симбиоз предоставляет огромные возможности для использования Интернета в своих проектах; ? Arduino MKR — серия плат, разработанная компанией Arduino, предоставляет различные варианты подключения к сети и управления питанием, в большой степени направлена на Интернет вещей (IoT); ? Arduino Nano 33 — серия плат Nano нового поколения. Платы имеют практически те же размеры, что и оригинал, но отличаются новыми процессорами, низким энергопотреблением и наличием беспроводных интерфейсов. Рассмотрим более подробно некоторые из этих плат. 2.2. Arduino Uno Плата Arduino Uno (рис. 2.1) построена на микроконтроллере ATmega328. В отличие от всех предыдущих ее версий, использовавших для связи по USB микроконтроллер FTDI USB, новая Arduino Uno использует с этой целью микроконтроллер ATmega8U2. Характеристики платы Arduino Uno представлены в табл. 2.1. Рис. 2.1. Плата Arduino Uno
22 Часть I. Arduino — общий обзор Таблица 2.1. Характеристики платы Arduino Uno Микроконтроллер Рабочее напряжение Входное напряжение (рекомендуемое) Входное напряжение (предельное) Цифровые входы/выходы Аналоговые входы Постоянный ток через вход/выход Постоянный ток для вывода 3,3 В Флеш-память ОЗУ EEPROM Тактовая частота ATmega328 5В 7-12 В 6-20 В 14 F из которых могут использоваться как выходы ШИМ) 6 40 мА 50 мА 32 Кбайт, при этом 0,5 Кбайт используются для загрузчика 2 Кбайт 1 Кбайт 16 МГц 2.3. Arduino Nano Плата Nano (рис. 2.2), построенная на микроконтроллере ATmega328 (Arduino Nano 3.0) или ATmegal68 (Arduino Nano 2jc), имеет небольшие размеры и может использоваться в лабораторных работах. Рис. 2.2. Плата Arduino Nano Arduino Nano способна получать питание через подключение USB Mini-B или от внешнего источника питания: нерегулируемого 6-20 В (вывод 30) или регулируемого 5 В (вывод 27). Источник с самым высоким напряжением выбирается автоматически. Характеристики платы Arduino Nano представлены в табл. 2.2.
Глава 2. Платы семейства Arduino и платы расширения для них 23 Таблица 2.2. Характеристики платы Arduino Nano Микроконтроллер Рабочее напряжение Входное напряжение (рекомендуемое) Входное напряжение (предельное) Цифровые входы/выходы Аналоговые входы Постоянный ток через вход/выход Постоянный ток для вывода 3,3 В Флеш-память ОЗУ EEPROM Тактовая частота ATmega168 или ATmega328 5В 7-12 В 6-20 В 14 F из которых могут использоваться как выходы ШИМ) 6 40 мА 50 мА 16 Кбайт (ATmega168) или 32 Кбайт (ATmega328), при этом 2 Кбайт используются для загрузчика 1 Кбайт (ATmega168) или 2 Кбайт (ATmega328) 512 байтов (ATmega168) или 1 Кбайт (ATmega328) 16 МГц 2.4. Arduino Pro Mini Плата Arduino Pro Mini (рис. 2.3) построена на микроконтроллере ATmegal68. Рис. 2.3. Плата Arduino Pro Mini Характеристики платы Arduino Pro Mini представлены в табл. 2.3. Таблица 2.3. Характеристики платы Atduino Pro Mini Микроконтроллер Рабочее напряжение Входное напряжение Цифровые входы/выходы Аналоговые входы АТтеда168 3,3 или 5 В (в зависимости от модели) 3,35-12 В (модель 3,3 В) или 5-12 В (модель 5 В) 14 F из которых могут использоваться как выходы ШИМ) 6
24 Часть I. Arduino — общий обзор Таблица 2.3 (окончание) Постоянный ток через вход/выход Флеш-память ОЗУ EEPROM Тактовая частота 40 мА 16 Кбайт B — используются для загрузчика) 1 Кбайт 512 байтов 8 МГц (модель 3,3 В) или 16 МГц (модель 5 В) Arduino Pro Mini может получать питание: через кабель FTDI, или от платы- конвертера, или от регулируемого источника питания 3,3 или 5 В (зависит от модели платформы) через вывод VCC, или от нерегулируемого источника через вывод RAW. Выводы питания: ? RAW — для подключения нерегулируемого напряжения; ? VCC — для подключения регулируемых 3,3 или 5 В; ? GND — выводы заземления. 2.5. Arduino LilyPad Плата Arduino LilyPad (рис. 2.4) разработана так, чтобы ее можно было зашить в ткань одежды вместе со встроенными источниками питания, датчиками, проводами и приводами. Рис. 2.4. Плата Arduino LilyPad Характеристики платы Arduino LilyPad представлены в табл. 2.4. Таблица 2.4. Характеристики платы Arduino LilyPad Микроконтроллер Рабочее напряжение Входное напряжение ATmega168 или ATmega328 2,7-5,5 В 2,7-5,5 В
Глава 2. Платы семейства Arduino и платы расширения для них 25 Таблица 2.4. Характеристики платы Arduino LilyPad Цифровые входы/выходы Аналоговые входы Постоянный ток через вход/выход Флеш-память ОЗУ EEPROM Тактовая частота 14 F из которых могут использоваться как выходы ШИМ) 6 40 мА 16 Кбайт (ATmega168) или 32 Кбайт (ATmega328), при этом 2 Кбайт используются для загрузчика 1 Кбайт (ATmega168) или 2 Кбайт (ATmega323) 512 байтов (ATmega168) или 1 Кбайт (ATmega328) 16 МГц 2.6. Arduino Mega2560 Плата Arduino Mega2560 (рис. 2.5) построена на микроконтроллере ATmega2560. Рис. 2.5. Плата Arduino Mega2560 Характеристики платы Arduino Mega2560 представлены в табл. 2.5. Таблица 2.5. Характеристики платы Arduino Mega2560 Микроконтроллер Рабочее напряжение Входное напряжение (рекомендуемое) АТтеда2560 5В 7-12 В
26 Часть /. Arduino — общий обзор Таблица 2.5 (окончание) Входное напряжение (предельное) Цифровые входы/выходы Аналоговые входы Постоянный ток через вход/выход Постоянный ток для вывода 3,3 В Флеш-память ОЗУ EEPROM Тактовая частота 6-20 В 54 A4 из которых могут использоваться как выходы ШИМ) 16 40 мА 50 мА 256 Кбайт, из которых 8 Кбайт используются для загрузчика 8 Кбайт 4 Кбайт 16 МГц 2.7. Arduino Leonardo Плата Arduino Leonardo (рис. 2.6) построена на базе микроконтроллера ATmega32U4, имеющего, в отличие от всех других микропроцессоров, встроенную поддержку для USB-соединения. Характеристики платы Arduino Leonardo представлены в табл. 2.6. Рис. 2.6. Плата Arduino Leonardo
Глава 2. Платы семейства Arduino и платы расширения для них 27 Таблица 2.6. Характеристики платы Arduino Leonardo Микроконтроллер Рабочее напряжение Входное напряжение (рекомендуемое) Входное напряжение (предельное) Цифровые входы/выходы Аналоговые входы Постоянный ток через вход/выход Постоянный ток для вывода 3,3 В Флеш-память ОЗУ EEPROM Тактовая частота ATmega32U4 5В 7-12 В 6-20 В 20 G из которых могут использоваться как выходы ШИМ) 12 40 мА 50 мА 32 Кбайт, из которых 4 Кбайт используются для загрузчика 2 Кбайт 1 Кбайт 16 МГц 2.8. Arduino Due Arduino Due (рис. 2.7) представляет собой первую плату Arduino на основе 32-битного микроконтроллера с ARM-ядром — процессора Atmel SAM3X8E ARM Cortex-M3. В отличие от других плат Arduino, максимальное рабочее напряжение, которое выдерживают входы/выходы Arduino Due, составляет 3,3 В. Рис. 2.7. Плата Arduino Due
28 Часть I. Arduino — общий обзор Характеристики платы Arduino Due представлены в табл. 2.7. Таблица 2.7. Характеристики платы Arduino Due Микроконтроллер Рабочее напряжение Входное напряжение (рекомендуемое) Входное напряжение (предельное) Цифровые входы/выходы Аналоговые входы Аналоговые выходы Постоянный ток через вход/выход Постоянный ток для вывода 3,3 В Постоянный ток для вывода 5 В Флеш-память ОЗУ Тактовая частота AT91SAM3X8E 3,3 В 7-12 В 6-20 В 54 A2 из которых могут использоваться как выходы ШИМ) 12 2(ЦАП) 50 мА 800 мА 800 мА 512 Кбайт 96 Кбайт (два банка: 64 и 32 Кбайт) 84 МГц 2.9. Arduino Yun Arduino Yun (рис. 2.8) — это плата на основе Arduino Leonardo, объединяющая в себе, как уже упоминалось ранее, достоинства двух платформ, поддерживаемых Свободным сообществом: Arduino и Linux. Получившийся симбиоз предоставляет огромные возможности для использования Интернета в своих проектах. Arduino-часть платы содержит микроконтроллер ATmega32U4, работающий на частоте 16 МГц. «Распиновка» Arduino Yun аналогична Arduino Leonardo, поэтому вместе с Arduino Yun вы можете использовать большинство плат расширения Arduino. Linux-часть платы Arduino Yun использует микрокомпьютер Atheros AR9331, работающий под управлением операционной системы Linino — специально подготовленной версии OpenWRT (популярного дистрибутива Linux для встраиваемых систем). Микрокомпьютер Atheros AR9331 работает на частоте 400 МГц, имеет 64 Мбайт оперативной и 16 Мбайт флеш-памяти, встроенные интерфейсы Wi-Fi и Ethernet, USB-host и слот для карты microSD. Linino содержит в себе пакетный менеджер opkg, который позволяет устанавливать большое количество Linux-приложений, а также интерпретатор языка Python 2.7, с помощью которого вы можете писать для Linino свои приложения.
Глава 2. Платы семейства Arduino и платы расширения для них 29 Рис. 2.8. Плата Arduino Yun Память для Linux-приложений может быть расширена с помощью съемного носителя (карты microSD или USB-флешки). 2.10. Arduino MKR WiFi 1010 Arduino MKR WiFi 1010 (рис. 2.9) -— это платформа из семейства MKR для разработчиков с минимальным опытом работы в сети, желающих создавать проекты IoT. Плата состоит из трех основных блоков: ? микроконтроллера ATSAMD21G18 с тактовой частотой 48 МГц и вычислительным ядром Cortex-M0+. Благодаря своей 32-битной архитектуре контроллер справляется с ресурсоемкими математическими вычислениями, обрабатывает аналоговые сигналы с большой точностью и воспроизводит музыку через встроенный ЦАП; ? модуля беспроводной связи NINA-W10 от компании u-blox со встроенным чипом ESP32. Модуль обеспечивает беспроводной обмен данными в диапазоне 2,4 ГГц по протоколам Wi-Fi и Bluetooth. Регулировка выходной мощности позволяет достичь оптимального соотношения между дальностью связи, скоростью передачи данных и энергопотреблением; ? крипточипа ЕСС508 для защиты передаваемых данных с использованием шифрования SHA-256. Крипточип дает возможность выполнять HTTPS-запросы без нагрузки на основной процессор. Поскольку платформа выполнена на архитектуре ARM Cortex-M0+, «родное» напряжение ее выводов — 3,3 В. То есть выходы для логической единицы выдают
30 Часть I. Arduino —- общий обзор Рис. 2.9. Плата Arduino MKRWiFi 1010 3,3 В и рассчитаны принимать напряжение не более этого. Большее напряжение повредит микроконтроллер. 2.11. Arduino Nano 33 Arduino Nano 33 — серия плат Nano нового поколения. Платы, как уже отмечалось ранее, имеют практически те же размеры, что и оригинал, но отличаются новыми процессорами, низким энергопотреблением и наличием беспроводных интерфейсов. К настоящему моменту выпущено четыре платы этой серии (рис. 2.10): Arduino Nano Every, Nano 33 IoT, Nano 33 BLE и Nano 33 BLE Sense. Рис. 2.10. Семейство Arduino Nano 33: Nano Every, Nano 33 IoT, Nano 33 BLE, Nano 33 BLE Sense (слева направо)
Глава 2. Платы семейства Arduino и платы расширения для них 31_ П В отличие от оригинальной платы Arduino Nano, несущей на борту 8-битный микроконтроллер Microchip ATmega328P, плата Arduino Nano Every построена на основе гораздо более мощного микроконтроллера Microchip ATmega4809 с микросхемой ATSAMD11 ARM Cortex МО и процессором для USB-to-serial связи. ? Плата Arduino Nano 33 IoT основана на микросхеме ATSAMD21 ARM Cortex- МО с поддержкой Wi-Fi и Bluetooth LE, предоставляемой встроенным микроконтроллером Espressif ESP32 в форме модуля NINA-W102 u-blox. Плата также оснащена 9-осевым инерциальным измерительным устройством (Inertial measurement unit, IMU), крипточипом для безопасного хранения сертификатов и общих ключей и совместима с новым облаком Arduino IoT. ? В отличие от Nano Every и Nano 33. IoT, плата Arduino Nano 33 BLE не основана на процессоре с микрочипом. Вместо этого она оснащена модулем u-blox NINA- В306, который, в свою очередь, построен на базе платформы Nordic nRF52840 архитектуры ARM Cortex-M4F. Плата Nano 33 BLE также несет на борту 9-осевой IMU. ? Плата Nano 33 BLE Sense построена на том же модуле u-blox NINA-B306, но — в дополнение к 9-осевой IMU — поставляется с гораздо большим набором датчиков: давления, влажности, температуры и света, а также датчиком жестов и встроенным микрофоном. 2.12. Платы расширения для Arduino Большую популярность плата Arduino приобрела не только из-за низкой стоимости, легкости разработки и программирования, но главным образом благодаря наличию плат расширения (так называемых шилдов\ добавляющих Arduino дополнительную функциональность. Шилды (кроме маленьких модулей и платы LilyPad) подключаются к Arduino с помощью имеющихся на них штыревых разъемов (рис. 2.11). Существует множество различных по функциональности шилдов— от простейших, предназначенных для макетирования, до сложных, представляющих собой отдельные многофункциональные устройства. Далее приведено краткое описание некоторых шилдов: ? Ethernet Shield — обеспечивает подключение к Интернету; ? ХВее Shield — обеспечивает при помощи модуля Maxstream Xbee Zigbee беспроводную связь нескольких устройств Arduino; ? MicroSD Shield — обеспечивает запись данных на карты microSD; ? МРЗ Shield — плата для воспроизведения звука в форматах Ogg Vorbis, MP3, ААС, WMA и MIDI и записи звука в формате Ogg Vorbis; ? Motor Shield — обеспечивает управление двигателями постоянного тока; ? GSM/GPRS Shield— позволяет отправлять SMS-сообщения, делать телефонные звонки, обмениваться данными по GPRS;
32 Часть I. Arduino — общий обзор Рис. 2.11. Модульная структура установки плат расширения для Arduino ? Cosmo WiFi Connect — обеспечивает организацию беспроводной сети стандарта ЕЕЕЕ 802.1 lb/g. Существуют также шилды: Video Overlay Shield — для наложения текста на аналоговое видео, EasyVR Arduino Shield— многоцелевой модуль распознавания речи, Music Shield — профессиональный аудиокодек и др. Количество плат расширения (шилдов) постоянно растет. Ознакомиться с их списком можно на официальном сайте проекта Arduino по адресу: http://www. arduino.cc/playground/Main/SimilarBoards#goShie.
ЧАСТЬ II 00 Среды разработки и язык программирования плат Arduino Глава 3. Среда разработки Arduino IDE Глава 4. Облачная среда разработки Arduino Create Глава 5. Программирование плат Arduino
ГЛАВА 3 Среда разработки Arduino IDE Разработка собственных приложений на базе плат, совместимых с архитектурой Arduino, осуществляется в бесплатной среде разработки Arduino IDE. Среда предназначена для написания, компиляции и загрузки собственных программ в память микроконтроллера, установленного на плате Arduino-совместимого устройства. Основу среды разработки составляет язык Processing/Wiring— это фактически обычный язык C++, дополненный простыми и понятными функциями для управления вводом/выводом на контактах. Существуют версии среды для операционных систем Windows, macOS и Linux. Последнюю версию среды Arduino A.8.13) и бета-версию Arduino (с экспериментальными возможностями) можно скачать со страницы загрузки официального сайта: http://arduino.cc/en/Main/Software. Примечание В главе 4 вашему вниманию представлена новая облачная среда разработки Arduino Create. 3.1. Установка Arduino IDE в Windows Отправляемся на страницу http://arduino.cc/en/Main/Software (рис. 3.1), выбираем версию для операционной системы Windows и скачиваем архивный файл. Он занимает чуть более 80 Мбайт и содержит все необходимое, в том числе и драйверы. По окончании загрузки распаковываем скачанный файл в удобное для себя место. Теперь необходимо установить драйверы. Подключаем плату Arduino к компьютеру. На ней должен загореться индикатор питания — зеленый светодиод. Windows начинает попытку установки драйвера, которая заканчивается сообщением: Программное обеспечение драйвера не было установлено. Открываем Диспетчер устройств. В составе устройств находим значок Arduino Uno — устройство отмечено восклицательным знаком. Щелкаем правой кнопкой мыши на значке Arduino Uno и в открывшемся окне выбираем пункт Обновить драйверы и далее пункт Выполнить поиск драйверов на этом компьютере. Указываем путь к драйверу — ту папку на компьютере, куда распаковывали скачанный
36^ Часть II. Среды разработки и язык программирования плат Arduino архив. Пусть это будет папка drivers каталога установки Arduino. Игнорируем все предупреждения Windows и получаем в результате сообщение: Обновление программного обеспечения для данного устройства завершено успешно. В заголовке окна будет указан и СОМ-порт, на который установлено устройство. Осталось установить и запустить среду разработки Arduino IDE. Рис. 3.1. Страница загрузки официального сайта Arduino 3.2. Установка Arduino IDE в Linux В Linux Ubuntu среда Arduino DDE устанавливается просто — она находится в репо- зитории стандартных приложений Linux. Выбираем Arduino IDE из списка доступных программ в меню Ubuntu: Приложения | Центр приложений Ubuntu | Загрузить приложение. В списке разделов выбираем Инструменты разработчика, в списке следующего уровня— Все приложения и в следующем открывшемся списке— Arduino IDE (рис. 3.2). Щелкаем левой кнопкой мыши на значке этой программы— справа от нее появляется кнопка Установить, нажимаем на эту кнопку, и среда устанавливается автоматически.
Глава 3. Среда разработки Arduino IDE 37 Рис. 3.2. Выбор программы из центра приложений Ubuntu 3.3. Настройка среды Arduino IDE Среда разработки Arduino (рис. 3.3) состоит из: О редактора программного кода; ? области сообщений; ? окна вывода текста; ? панели инструментов с кнопками часто используемых команд; О нескольких меню. Примечание Все примеры работы с Arduino IDE в этом издании книги показаны в наиболее распространенной версии среды — 1.8.5. Программа, написанная в среде Arduino, носит название скетч. Скетч пишется в текстовом редакторе, который имеет цветовую подсветку создаваемого программного кода. Во время сохранения и экспорта проекта в области сообщений появляются пояснения и информация об ошибках. Окно вывода текста показывает сообщения Arduino, включающие полные отчеты об ошибках и другую информацию. Кнопки панели инструментов позволяют проверить и записать программу, создать, открыть и сохранить скетч, открыть мониторинг последовательной шины. Разрабатываемым скетчам дополнительная функциональность может быть добавлена с помощью библиотек, представляющих собой специальным образом оформленный программный код, реализующий некоторый функционал, который можно подключить к создаваемому проекту. Специализированных библиотек существует множество. Обычно библиотеки пишутся так, чтобы упростить решение той или
38 Часть II. Среды разработки и язык программирования плат Arduino Рис. 3.3. Arduino IDE — среда разработки иной задачи и скрыть от разработчика детали программно-аппаратной реализации. Среда Arduino IDE поставляется с набором стандартных библиотек: Serial, EEPROM, SPI, Wire и др. Они находятся в подкаталоге libraries каталога установки Arduino. Необходимые библиотеки могут быть также загружены с различных ресурсов (см. главу 7). Пацка загруженной библиотеки копируется в каталог стандартных библиотек (подкаталог libraries каталога установки Arduino). Внутри каталога с именем библиотеки находятся файлы *.срр, *.h. Многие библиотеки снабжаются примерами, расположенными в папке examples каталога установки Arduino. Если библиотека установлена правильно, то она появляется в меню Скетч | Import Library. Выбор библиотеки в меню приведет к добавлению в исходный код строки: #include <имя библиотеки.h> Эта директива подключает заголовочный файл с описанием объектов, функций и констант библиотеки, которые теперь могут быть использованы в проекте. Среда Arduino будет компилировать создаваемый проект вместе с указанной библиотекой. Электронный архив Необходимые для работы с проектами книги подгружаемые библиотеки размещены в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
Глава 3. Среда разработки Arduino IDE 39 Рис. 3.4. Arduino IDE — выбор платы Перед загрузкой скетча требуется задать необходимые параметры: в меню Сервис | Плата (Tools | Board) выбрать используемую плату (рис. 3.4) и в меню Сервис | Последовательный порт (Tools | Serial Port) — задействованный последовательный порт (рис. 3.5). Современные платы Arduino перед загрузкой перезагружаются автоматически. На старых платах необходимо нажать кнопку перезагрузки. На большинстве плат во время процесса загрузки будут мигать светодиоды RX и ТХ. При загрузке скетча используется загрузчик (bootloader) Arduino — небольшая программа, загружаемая в микроконтроллер на плате. Она позволяет загружать программный код без использования дополнительных аппаратных средств. Работа загрузчика распознается по миганию светодиода на цифровом выводе D13. Монитор последовательного порта (Serial Monitor) отображает данные, посылаемые в плату Arduino (через порт USB или порт последовательной шины). Для отправки данных необходимо ввести в соответствующее поле текст и нажать кнопку Послать (Send) или клавишу <Enter> (рис. 3.6). Предварительно следует из выпадающего списка выбрать скорость передачи, соответствующую значению serial.begin в скетче. На macOS или ОС Linux при подключении мониторинга последовательной шины плата Arduino будет перезагружена (скетч начнется сначала).
40 Часть II. Среды разработки и язык программирования плат Arduino Рис. 3.5. Arduino IDE — выбор последовательного порта Рис. 3.6. Arduino IDE — монитор последовательного порта
ГЛАВА 4 Облачная среда разработки Arduino Create Arduino Create — это новая платформа, призванная заменить существующую среду разработки Arduino IDE (см. главу 3). Как пишут сами создатели платформы: «Это важный шаг в экосистеме Arduino, и мы надеемся изменить способ взаимодействия с вашими проектами и сообществом». Arduino Create сочетает в себе новый Arduino Web Editor (облачный редактор Arduino), инструменты для быстрого старта, доступ к Arduino-магазину и форуму, а также Project Hub, базирующийся на ресурсе Hackster.io, и сетевой облачный сервис Arduino Cloud. Основная идея новой платформы состоит в том, что разработчик теперь имеет возможность писать код и загружать скетчи к любой плате Arduino непосредственно из браузера с помощью облачного редактора Arduino Web Editor — без необходимости что-либо устанавливать на свой компьютер. При использовании новой платформы папка со скетчами Sketchbook будет храниться в облаке Arduino и станет доступна с любого устройства. Возможности Arduino Create покрывают практически всю область разработки: вы сможете создавать код прямо у себя в браузере и отправлять его на свою плату Arduino, читать документацию и описание лучших способов работы с Arduino, общаться с коллегами, исследовать проекты других разработчиков. 4.1. Начало работы в среде Arduino Create Итак, заходим на сайт Arduino Create: https://create.arduino.cc/ (рис. 4.1). Сначала там необходимо зарегистрироваться (кнопка SIGN Ш). На странице регистрации заполняем форму и нажимаем на кнопку CREATE ACCOUNT (рис. 4.2). Активация профиля осуществляется при переходе по ссылке в письме, пришедшем на указанную вами почту. Войдя в профиль, мы можем работать на сайте. Выбираем пункт Arduino Web Editor. Для дальнейшей работы необходимо установить плагин arduino-create-agent (рис. 4.3). Этот плагин позволит портам вашего компьютера общаться с веб-редактором в браузере, загружать скетчи из браузера в .платы, подключенные к USB или к сети, использовать другие облачные сервисы.
42 Часть II. Среды разработки и язык программирования плат Arduino Рис. 4.1. Стартовая страница сервиса Arduino Create Рис. 4.2. Форма регистрации сервиса Arduino Create
Глава 4. Облачная среда разработки Arduino Create 43 Рис. 4.3. Форма загрузки плагина сервиса Arduino Create 4.2. Загрузка и выполнение пробного скетча После установки плагина (рис. 4.4) выберем пример Blynk (Examples | Basics | Biynk) и загрузим его в свою плату Arduino (рис. 4.5). Не забываем также выбрать тип платы Arduino и порт подключения (рис. 4.6). Рис. 4.4. Плагин arduino-create-agent установлен
44 Часть II. Среды разработки и язык программирования плат Arduino Рис. 4.5. Загрузка примера BIynk в плату Arduino Рис. 4.6. Выбор типа платы и порта подключения
ГЛАВА 5 Программирование плат Arduino Материал этой главы основан ца переводе с официального сайта проекта Arduino (http://arduino.cc) и представлен по лицензии Creative Commons Attribution- ShareAlike 3.0 License (http://creativecommons.Org/licenses/by-sa/3.0/deed.ru). 5.1. Базовые знания 5.1.1. Цифровые выводы Выводы плат Arduino могут работать и как входы, и как выходы. Аналоговые входы Arduino (на микроконтроллере ATmega) могут конфигурироваться и работать так же, как и цифровые порты ввода/вывода. Выводы Arduino настроены как порты ввода, поэтому для них не требуется декларации в функции pinMode (). Сконфигурированные порты ввода находятся в высо- коимпедансном состоянии. Это означает, что порт ввода дает слищком малую нагрузку на схему, в которую он включен. Для перевода порта ввода из одного состояния в другое необходимо малое значение тока. Если к выводу ничего не подключено, то значения на нем будут принимать случайные величины, наводимые электрическими помехами. Поэтому если на порт ввода не поступает сигнал, то рекомендуется задать порту какое-либо известное состояние. Это делается добавлением подтягивающих резисторов 10 кОм, подключающих вход либо к питанию +5 В, либо к земле. Микроконтроллер ATmega имеет программируемые встроенные подтягивающие резисторы 20 кОм. Программирование этих резисторов осуществляется так: pinMode(pin, INPUT); // назначить выводу порт ввода digitalWrite(pin, HIGH); // включить подтягивающий резистор Выводы, сконфигурированные как порты вывода, находятся в низкоимпедансном состоянии. Эти выводы могут пропускать через себя весьма большой ток. Так, выводы микросхемы ATmega могут быть источником тока до 40 мА, однако такого значения тока все же недостаточно для большинства реле, соленоидов и двигателей.
46^ Часть //. Среды разработки и язык программирования плат Arduino Короткие замыкания выводов Arduino или попытки подключить энергоемкие устройства могут повредить выходные транзисторы вывода или весь микроконтроллер ATmega. 5.1.2. Аналоговые входы Микроконтроллеры ATmega, используемые в Arduino, содержат шестиканальный аналого-цифровой преобразователь (АЦП). Разрешение преобразователя составляет 10 битов, что позволяет на выходе получать значения от 0 до 1023. Аналоговые входы могут использоваться как цифровые выводы портов ввода/вывода, при этом они имеют номера от 14 до 19: pinModeA4,OUTPUT); digitalWriteA4, HIGH); Для вывода, работавшего ранее как цифровой порт вывода, команда anaiogRead будет работать некорректно. В этом случае рекомендуется сконфигурировать его как аналоговый вход. 5.1.3. Широтно-импульсная модуляция Широтно-импульсная модуляция (ШИМ)— это операция получения изменяющегося аналогового значения посредством цифровых устройств. Подавая на выход сигнал, состоящий из высоких и низких уровней, мы моделируем напряжение между максимальным значением E В) и минимальным @ В). Длительность включения максимального значения называется шириной импульса. Для получения различных аналоговых величин ширину импульса изменяют. В результате на выходе будет получена величина напряжения, равная площади под импульсами (рис. 5.1). Вызов функции anaiogwrite () с масштабом 0-255 означает, что значение anaiogwriteB55) будет соответствовать 5 В A00%-ный рабочий цикл — постоянное включение 5 В), а значение anaiogwrite A27) — 2,5 В E0%-ный рабочий цикл). 5.1.4. Память в Arduino В микроконтроллерах ATmegal68, ATmega328, ATmegal280, ATmega2560, используемых на платформах Arduino, существует три вида памяти: ? флеш-память — используется для хранения скетчей; ? ОЗУ (статическая оперативная память) — служит для хранения и работы пере- . менных; ? EEPROM (энергонезависимая память) — применяется для хранения постоянной информации. Флеш-память и EEPROM являются энергонезависимыми видами памяти (данные сохраняются при отключении питания). ОЗУ представляет собой энергозависимую память.
Глава 5. Программирование плат Arduino 47 1 _- _ _ _ __ 90% 10% 50% II И J Эквивалентное постоянное напряжение | Эквивалентное постоянное напряжение | ¦; тс зт л ц Эквив; алентн юепо стоян^ юена пряжение ] о Рис. 5.1. Широтно-импульсная модуляция Микроконтроллер ATmegal68 имеет: ? 16 Кбайт флеш-памяти B Кбайт используются для хранения загрузчика); ? 1024 байта ОЗУ; ? 512 байтов EEPROM. Для ATmega328 эти показатели следующие: ? 32 Кбайт флеш-памяти B Кбайт используются для хранения загрузчика); ? 2 Кбайт ОЗУ; ? 1024 байта EEPROM.
48^ Часть II. Среды разработки и язык программирования ллат Arduino Для ATmegal280 эти показатели следующие: ? 128 Кбайт флеш-памяти B Кбайт используются для хранения загрузчика); ? 8 Кбайт ОЗУ; ? 4096 байтов EEPROM. Для ATmega2560 эти показатели следующие: ? 256 Кбайт флеш-памяти B Кбайт используются для хранения загрузчика); ? 16 Кбайт ОЗУ; ? 9182 байта EEPROM. При отсутствии свободного места в ОЗУ могут произойти сбои программы. 5.2. Структура программы Arduino программируется на языке Wiring, которого на самом деле не существует, как не существует и компилятора Wiring, — написанные на Wiring программы преобразуются в программу на языке C/C++ и затем компилируются компилятором AVR-GCC. Фактически используется специализированный для микроконтроллеров AVR вариант C/C++. 5.2.1. Функции setupf) и 1оор() Базовая структура программы для Arduino состоит по меньшей мере из двух обязательных частей: функций setup () и loop (). Перед функцией setup () идет объявление переменных, подключение библиотек. Функция setup о запускается один раз после каждого включения питания или сброса платы Arduino. Она используется для инициализации переменных, установки режима работы портов и прочих подготовительных для основного цикла программы действий. Функция setup () обязательно должна быть включена в программу, даже если не выполняет никаких действий. Функция loop () в бесконечном цикле выполняет основную работу программы — последовательно исполняет команды, которые описаны в ее теле. Пример простейшей программы представлен в листинге 5.1. void setup() { Serial.begin(9600); } void loop() { Serial.println (inillis ()) ; delayA000);
Глава 5. Программирование плат Arduino 5.3. Синтаксис и операторы 5.3.1. Управляющие операторы 5.3.1.1. Оператор /У (условие) и операторы сравнения ==, /=,<,> Оператор if. используется в сочетании с операторами сравнения. Он проверяет, достигнута ли истинность условия — например, превышает ли входное значение заданное число. Формат оператора if следующий: if (someVariabJe > 50){ // выполнять действия } Здесь программа проверяет, больше ли значение somevariabie чем 50 или нет. Если да, то выполняются определенные действия. Говоря иначе, если выражение в круглых скобках истинно, выполняются операторы внутри фигурных скобок. Если нет, программа пропускает этот код. Выражения, которые вычисляются внутри круглых скобок, могут состоять из одного или нескольких операторов. Операторы сравнения: ? х == у (х равно у); О х != у (х не равно у); ? х < у (х меньше чем у); ? х > у (х больше чем у); ? х <= у (х меньше чем или равно у); ? х >= у (х больше чем или равно у). 5.3.1.2. Оператор if.. .else Конструкция if.. .else предоставляет больший контроль над процессом выполнения кода, чем базовый оператор if, — она позволяет сделать выбор «либо, либо». Например: if (pinInput=HIGH) {doFunlO;} else {doFun2();} В отличие от одиночного оператора if, оператор if.. .else дает возможность осуществлять сразу несколько взаимоисключающих проверок. Каждая проверка позволяет переходить к следующему за ней оператору не раньше, чем получит логический результат истина. Когда проверка с результатом истина найдена, запускается вложенный в нее блок операторов, и затем программа игнорирует все следующие строки в конструкции if.. .else. Если ни одна из проверок не получила результат истина, по умолчанию выполняется блок операторов в else, если по-
50 Часть II. Среды разработки и язык программирования плат Arduino следний присутствует, и устанавливается действие по умолчанию. Конструкция else...if может быть использована как с заключительным else, так и без него, и наоборот. Допускается неограниченное число таких переходов else...if (листинг 5.2). if (pinAnaloglnput < 100) {doFunlO;} else if (pinAnaloglnput >= 150) {doFun2();} else {doFun3();> Другой способ создания переходов со взаимоисключающими проверками использует оператор switch case (см. далее). 5.3.1.3. Оператор for Конструкция for служит для повторения блока операторов, заключенных в фигурные скобки. Она имеет счетчик приращений, обычно использующийся для определения количества повторений и завершения цикла. Оператор for подходит для любых повторяющихся действий и часто применяется в сочетании с массивами коллекций данных/выводов. Заголовок цикла for состоит из трех частей: for (initialization; condition; increment) {операторы, выполняющиеся в цикле} Инициализация (initialization) выполняется самой первой и один раз. Каждый раз в цикле проверяется условие (condition), и если оно верно, выполняется блок операторов и приращение (increment), затем условие проверяется вновь. Когда логическое значение условия становится ложным, цикл завершается. В листинге 5.3 приведен пример затемнения светодиода с использованием ШИМ-вывода. // Затемнение светодиода с использованием ШИМ-вывода int PWMpin = 10; // Светодиод последовательно с R=470 Ом на 10 выводе void setup() {;} void loop () { for (int i=0;.i <= 255; i { analogWrite(PWMpin, i); delayA0);
Глава 5. Программирование плат Arduino 51_ 5.3.1.4. Оператор switch Конструкция switch.. .case управляет процессом выполнения программы, позволяя программисту задавать альтернативный код, который будет выполняться при разных условиях. Оператор switch сравнивает значение переменной со значением, определенным в операторах case. Когда найден оператор case, значение которого равно значению переменной, выполняется программный код, записанный в этом операторе. Ключевое слово break является командой выхода из оператора case и обычно используется в конце каждого case. Без оператора break оператор switch будет продолжать вычислять следующие выражения, пока не достигнет break или конца оператора switch. Синтаксис команды switch.. .case представлен в листинге 5.4. switch (var) { case label1: // код для выполнения break; case Iabel2: // код для выполнения break; case Iabel3: // код для выполнения break; default: // код для выполнения break; Параметры: ? var — переменная, которая вычисляется для сравнения с вариантами в case; ? label — значение, с которым сравнивается значение переменной. 5.3.1.5. Оператор while Оператор while будет вычислять в цикле непрерывно и бесконечно до тех пор, пока выражение в круглых скобках не станет равно логическому ложно. Что-то должно изменять значение проверяемой переменной, иначе выход из цикла while никогда не будет достигнут. Это изменение может происходить как в программном коде, например, при увеличении переменной, так и во внешних условиях, например, при тестировании датчика. Синтаксис команды следующий: while(выражение) { // операторы
52 Часть II. Среды разработки и язык программирования плат Arduino Пример использования оператора while представлен в листинге 5.5. var i=0; while($i<100) { // операторы 5.3.1.6. Оператор do...while Цикл do работает так же, как и цикл while, за исключением того, что условие проверяется в конце цикла. Таким образом, цикл do будет всегда выполняться хотя бы один раз. Пример использования оператора do ... while представлен в листинге 5.6. do { delayE0); // подождать, пока датчики стабилизируются х = readSensors(); // проверить датчики } while (х < 100); 5.3.1.7. Оператор break Оператор break используется для принудительного выхода из циклов do, for или while, не дожидаясь завершения цикла по условию. Он также применяется для выхода из оператора switch. Пример приведен в листинге 5.7. for (х = 0; х < 255; х ++) { digitalWrite(PWMpin, x); sens = analogRead(sensorPin); if (sens > threshold) { // выходим из цикла, если есть сигнал с датчика х = 0; break; } delayE0); 5.3.1.8. Оператор continue Оператор continue пропускает оставшиеся операторы в текущем шаге цикла. Вместо них выполняется проверка условного выражения цикла, которая происходит при каждой следующей итерации. Пример приведен в листинге 5.8.
Глава 5. Программирование плат Arduino 53 for (x = 0; х < 255; х ++) { if (х > 40 && х < 120) { // если истина, то прыгаем сразу на следующую итерацию цикла continue; } digitalWrite(PWMpin, x); delayE0); 5.3.1.9. Оператор return Оператор return прекращает вычисления в функции и возвращает значение из прерванной функции в вызывающую, если это нужно. Пример возврата значения из функции в зависимости от значения на входе аналогового входа представлен в листинге 5.9. int checkSensor() { if (analogRead(O) > 200) return 1; else{ return 0; 5.3.2. Синтаксис 5.3.2.1.; (semicolon, точка с запятой) Точка с запятой ; — используется для обозначения конца оператора. int a = 13; 5.3.2.2. О (curly braces, фигурные скобки) Фигурные скобки {} — важный элемент языка программирования С. Открывающая скобка { должна всегда сопровождаться закрывающей скобкой }. Это условие известно как парность (симметричность) фигурных скобок. Основные способы использования фигурных скобок: ? функции: • void Название Функции (тип данных аргумента) { оператор(ы) };
54 Часть II. Среды разработки и язык программирования плат Arduino ? циклы: • while (логическое выражение){ оператор(ы)}; • do { оператор(ы)} while (логическое выражение); • for (инициализация; условие окончания цикла; приращения цикла) { оператор (ы)} ;. ? условные операторы: • if (логическое выражение) {оператор(ы)}. 5.3.2.3. Комментарии: //(single line comment, однострочный), /* V (multi-line comment, многострочный) Комментарии — это строки в программе, которые используются для информирования вас самих или других о том, как работает программа. Они игнорируются компилятором и не занимают места в памяти микроконтроллера. Есть два способа пометить строку как комментарий: ? однострочный комментарий — // ; ? многострочный комментарий — /* ... */ . Пример приведен в листинге 5.10. х = 5; // Это комментарий в одной строке. Все после двойного // слеша - комментарий до конца строки /* это многострочный комментарий - используйте его для закомментирования целых кусков кода */ 5.3.3. Арифметические операторы 5.3.3.1. = (assignment, оператор присваивания) Присваивает переменной слева от оператора значение переменной или выражения, находящееся справа (листинг 5.11). int sensVal; // объявление переменной типа integer senVal=analogRead@); // присваивание переменной sensVal значение, // считанное с аналогового входа 0 Переменная слева от оператора присваивания (±) должна быть способна сохранить присваиваемое значение. Если оно выходит за диапазон допустимых значений, то сохраненное значение будет не верно. Необходимо различать оператор присваивания (=) и оператор сравнения (==) — двойной знак равенства, который осуществляет проверку на равенство.
Глава 5. Программирование плат Arduino 55_ 5.3.3.2. + (сложение), - (вычитание), * (умножение), /(деление) Операторы +, -, * и / возвращают результат выполнения соответствующих арифметических действий над двумя операндами. Возвращаемый результат будет зависеть от типа данных операндов — например, 9/4 возвратит 2, т. к. операнды 9 и 4 имеют тип int. Также надо следить за тем, чтобы результат не вышел за диапазон допустимых значений для используемого типа данных. Так, например, сложение I с переменной типа int и значением 32 767 возвратит -32 768. Если операнды имеют разные типы, то для вычислений будет использован тип с более «широким» диапазоном. Если один из операндов имеет тип float или double, то для вычислений будет использована арифметика «с плавающей запятой». 5.3.3.3. % (modulo) Возвращает остаток от деления одного целого (int) операнда на другой. Примеры: х = 9 % 5; // х имеет значение 4 х=5%5; // х имеет значение О Нельзя применить к типу float. 5.3.4. Операторы сравнения Операторы сравнения: ? х == у (х равно у); ? х ! = у (х не равно у); ? х < у (х меньше чем у); ? х > у (х больше чем у); ? х <= у (х меньше чем или равно у); ? х >= у (х больше чем или равно у). 5.3.5. Логические операторы Логические операторы чаще всего используются в проверке условия оператора if. 5.3.5.1. && (логическое И) Истина, если оба операнда истина (true). Пример: if (digitalReadB) = HIGH && digitalReadC) == HIGH) Serial.println("ok"); 5.3.5.2. || (логическое ИЛИ) Истина, если хотя бы один операнд истина. Пример: if (digitalReadB) = || digitalReadC) == HIGH) Serial.println("ok");
56^ Часть II. Среды разработки и язык программирования плат Arduino 5.3.5.3. / (логическое отрицание) Истина, если операнд false, и наоборот. Пример: if (!(digitalReadB)== HIGH)) Serial.println("ok"); 5.3.6. Унарные операторы 5.3.6.1. ++ (увеличение значения), /— (уменьшение значения) Унарные (имеющие один операнд) операторы ++ и — соответственно увеличивают и уменьшают значение переменной (листинг 5.12). х++; // увеличивает значение х на единицу и возвращает старое значение х ++х; // увеличивает значение х на единицу и возвращает новое значение х х— ; // уменьшает значение х на единицу и возвращает старое значение х —х ; // уменьшает значение х на единицу и возвращает новое значение х 5.3.6.2. +=,-=,*=, /= Короткий способ записи арифметических действий над переменной и одним операндом (листинг 5.13). х += у; // эквивалент записи х = х + у; х -= у; // эквивалент записи х = х - у; х *= у; // эквивалент записи х = х * у; х /= у; // эквивалент записи х = х / у; 5.4. Данные 5.4.1. Типы данных Компилятор Arduino определяет следующие типы данных: boolean, char, byte, int, unsigned int, long, unsigned 'long, float, double, string, массив (array), void. Рассмотрим эти типы данных более подробно. 5.4.1.1. boolean Логический (булевый) тип данных — boolean. Может принимать одно из двух значений: true или false. Данные типа boolean занимают в памяти один байт.
Глава 5. Программирование плат Arduino 57_ 5.4.1.2. char Переменная типа char занимает 1 байт памяти и может хранить один алфавитно- цифровой символ (литеру). При объявлений литеры используются одиночные кавычки: fAf (двойные кавычки используются при объявлении строки символов — ТИП string: "ABC"). Символ хранится в памяти как число, соответствующее коду символа в таблицу кодировки символов ASCII. Так как символ хранится как число, в памяти над ним возможно производить арифметические действия (например, 'A' + i будет 66, т. к. ASCII код для fAf — 65). Тип char знаковый тип, т. е. число (код), хранящийся в памяти, может принимать значения от -128 до 127. Если необходима знаковая однобайтовая переменная, используйте ТИП byte. Пример: char myChar = 'A1; char myChar =65; // Варианты эквивалентны 5.4.1.3. byte Хранит 8-битовое числовое значение без десятичной точки. Имеет диапазон от О до 255. Пример: byte someVariable=150; // объявление переменной someVariable, // имеющей тип byte 5.4.1.4. int Тип данных int (от англ. integer — целое число) — один из наиболее часто используемых типов данных для хранения чисел. int занимает 2 байта памяти и может хранить числа от -32 768 до 32 767. Для размещения отрицательных значений int использует так называемый дополнительный код представления числа. Старший бит указывает на отрицательный знак числа, остальные биты инвертируются с добавлением 1. Arduino-компилятор сам заботится о размещении в памяти и представлении отрицательных чисел, поэтому арифметические действия над целыми числами производятся как обычно. Когда переменная типа int вследствие арифметической операции достигает своего максимального значения, она «перескакивает» на самое минимальное значение, и наоборот (листинг 5.14). int х; х = -32,768; х = х - 1; //х теперь равно 32,161
58 Часть II. Среды разработки и язык программирования плат Arduino х = 32,767; х = х + 1; // х теперь равно -32,768 5.4.1.5. unsigned int Тип данных unsigned int — беззнаковое целое число, так же, как и тип int (знаковое), занимает в памяти 2 байта. Но, в отличие от int, тип unsigned int может хранить только положительные целые числа в диапазоне от 0 до 65 535. Отличие кроется в том, как unsigned int использует старший бит, иногда называемый знаковым битом. Если старший бит равен 1, то для типа int компилятор Arduino считает, что это число отрицательное, а остальные 15 битов несут информацию о модуле целого числа в дополнительном коде представления числа, в то время как unsigned int использует все 16 битов для хранения модуля числа. Когда переменная типа unsigned int вследствие арифметической операции достигает своего максимального значения, она «перескакивает» на самое минимальное значение, и наоборот (листинг 5.15). unsigned int x; х = 0; х = х - 1; //х теперь равна 65535 х = х + 1; // х теперь 0 5.4.1.6. long Тип данных long используется для хранения целых чисел в расширенном диапазоне от -2 147 483 648 до 2 147 483 647. long занимает 4 байта в памяти. Пример: long varl = -178000; 5.4.1.7. unsigned long unsigned long используется для хранения положительных целых чисел в диапазоне от 0 до 4 294 967 295 и занимает 32 бита D байта) в памяти. Пример вывода в миллисекундах (мс) с начала выполнения программы приведен в листинге 5.16. void loop() { Serial.print("Time: "); time = millis (); // выводит время, прошедшее с момента начала выполнения программы Serial.println(time); functionl();
Глава 5. Программирование плат Arduino 5.4.1.8. float Тип данных float служит для хранения чисел с плавающей запятой. Этот тип часто используется для операций с данными, считываемыми с аналоговых входов. Диапазон значений: от -3,4028235Е+38 до 3,402823 5Е+3 8. Переменная типа float занимает 32 бита D байта) в памяти. Тип float имеет точность 6-7 знаков (имеются в виду все знаки, а не только мантисса). Обычно для увеличения точности используют другой тип — double, но на платформе Arduino double и float имеют одинаковую точность. 5.4.1.9. double Тип данных double, в отличие от большинства языков программирования, на платформе Arduino имеет ту же точность, что и тип float, и занимает также 4 байта памяти. Тип double поддерживается в Arduino для совместимости кода с другими платформами. 5.4.1.10. string — текстовые строки Текстовые строки в Arduino объявляются как массив (array) типа char (символов, литер), оканчивающийся символом «конца строки». Возможны следующие варианты объявления текстовых строк: ? объявить массив символов без присваивания значений; ? объявить массив символов и присвоить значения всем элементам, кроме последнего, — компилятор Arduino автоматически добавит символ конца строки; ? явно объявить завершающий символ; ? инициализировать массив строковой константой в двойных кавычках. Компилятор автоматически задаст требуемый размер на массив, равный количеству символов плюс завершающий символ; ? инициализировать массив с явным заданием размера и присвоением строковой константы; ? инициализировать массив с явным заданием дополнительного размера (с запасом), фактически превышающего размер строковой константы при начальном присвоении. В листинге 5.17 приведены варианты объявления и присвоения строк. char Svtrl[15]; char Str2[8] = {faf,fr1, fdf, fuf char Str3[8] *= {faf ,'r', fdf, fuf char Str4[ ] = "arduino"; char#Str5[8] = "arduino"; char Str6[15] = "arduino";
60 Часть II. Среды разработки и язык программирования плат Arduino Обычно строки оканчиваются нулевым символом (код 0 в ASCII). Это позволяет функциям (таким как Serial.print о) выявлять окончание строки. В противном случае могут считаться байты памяти, не принадлежащие переменной. Массив символов, выделяемый под строку, должен иметь один дополнительный элемент для символа конца строки. Если объявить строку без символа окончания строки, то это приведет к некорректной работе функций, оперирующих строками. Строки всегда объявляются внутри двойных кавычек: "Abe". При работе с большими объемами текстовой информации бывает удобно использовать массивы строк. Так как строки сами по себе массивы, массив строк будет двумерным массивом. В примере, приведенном в листинге 5.18, символ звездочки после объявления типа char* указывает на то, что мы имеем дело с массивом указателей. Это необходимо для задания двумерного массива. char* myStrings[]={"string 1", "string 2", "string 3","string 4", "string 5","string 6"}; void setup() {Serial.begin(9600);} void loopO { for (int i = 0; i < 6; i++){ Serial.println(myStrings[i]); delayE00); 5.4.1.11. Массивы Массивы (arrays) — именованный набор однотипных переменных с доступом к отдельным элементам по их индексу. Существует несколько вариантов объявления массива: ? массив может быть объявлен без непосредственной инициализации элементов массива: int mylnts[6]; П массив может быть объявлен без явного задания размера. Компилятор сам подсчитает фактическое количество элементов и создаст в памяти массив необходимого размера: int myPins[] = {2, 4, 8, 3, 6}; О при объявлении массива размер может быть задан явно, одновременно с инициализацией элементов массива. При создании массива типа char необходим дополнительный элемент массива для нулевого символа:
Глава 5. Программирование плат Arduino 61_ int mySensVals[6] = {2, 4, -8, 3, 2}; char message[6] = "hello"; Индексация массива начинается с 0. Присваивание значения элементу массива происходит следующим образом: mySensVals[0] = 10; Получение значения массива: х = mySensVals[4]; Чаще всего для перебора элементов цикла применяется цикл for, счетчик цикла используется как индекс для доступа к каждому элементу массива. Например, для вывода массива через последовательный порт (serial) можно задать следующий код: int i; for (i = 0; i < 5; i = i + 1) { Serial.println(myPins[i]); } 5.4.1.12. void Ключевое слово void используется при объявлении функций, если функция не возвращает никакого значения при ее вызове. 5.4.2. Константы Константы — предопределенные значения. Они используются, чтобы делать программы более легкими для чтения. Объявление констант (а также базовых макросов и функций) можно посмотреть в файле \hardware\arduino\cores\arduino\wiring.h. Рассмотрим некоторые константы: ? true/false— это булевы константы, определяющие логические уровни, false легко определяется как 0 (ноль), a true — как 1, но может быть и чем-то другим, отличным от нуля. Поэтому -1, 2 и 200 — это все тоже определяется как true. #define true 0x1 #define false 0x0 ? high/low — уровни сигналов порта high и low: tdefine HIGH 0x1 #define LOW 0x0 ? input/output — настройка цифровых портов на ввод (input) и вывод (output) сигналов: #define INPUT 0x0 #define OUTPUT 0x1 Цифровые порты могут использоваться на ввод или вывод сигналов. Изменение порта с ввода на вывод производится при помощи функции pinMode (): pinModeA3, OUTPUT); // 13 вывод будет выходом pinModeA2, INPUT); // 12 - входом
62_ Часть II. Среды разработки и язык программирования плат Arduino В программе можно создавать собственные константы: #define LEFT 0x95 tdefine MESS_LEFT "поворот влево" 5.4.3. Переменные Переменные •*— это способ именовать и хранить числовые значения для последующего использования их программой. Переменные представляют собой значения, которые могут последовательно меняться, в отличие от констант, чье значение никогда не меняется. Переменные нужно декларировать (объявлять). Следующий код объявляет переменную inputvariabie, а затем присваивает ей значение, полученное от 2-го аналогового порта: int inputVariable=O; inputVariable=analogReadB); Переменные могут быть названы любыми именами, которые не являются ключевыми словами языка программирования Arduino. 5.4.3.1. Объявление переменных Все переменные должны быть задекларированы до того, как они могут использоваться. Объявление переменной означает определение типа ее значения: int, long, float и т. д., задание уникального имени переменной, и дополнительно ей можно присвоить начальное значение. Все это следует делать только один раз в программе, но значение может меняться в любое время при использовании арифметических или других разных операций. Следующий пример показывает, что объявленная переменная inputvariabie имеет тип int, и ее начальное значение равно нулю. Это называется простым присваиванием. int inputvariabie = 0; Переменная может быть объявлена в разных местах программы, и то, где это сделано, определяет, какие части программы могут использовать переменную. 5.4.3.2. Границы переменных Переменные могут быть объявлены в начале программы перед void setup (), локально внутри функций и иногда в блоке выражений, таком как цикл for. Место, где объявлена переменная, определяет ее границы (область видимости), т. е. возможность некотррых частей программы ее использовать. ? Глобальные переменные таковы, что их могут видеть и использовать любые функции и выражения программы. Такие переменные декларируются в начале программы перед функцией setup (). ? Локальные переменные определяются внутри функций или таких частей, как цикл for. Они видимы и могут использоваться только внутри функции, в кото-
Глава 5. Программирование плат Arduino 63^ рой объявлены. Таким образом, может существовать несколько переменных с одинаковыми именами в разных частях одной программы, которые содержат разные значения. Уверенность, что только одна функция имеет доступ к ее переменной, упрощает программу и уменьшает потенциальную опасность возникновения ошибок. 5.4.4. Преобразование типов данных 5AAA.char() char () приводит значение к типу char. Синтаксис: char(х); где х — переменная любого типа. 5.4.4.2. byteO byte () приводит значение к типу byte. Синтаксис: byte(x); где х — переменная любого типа. 5.4.4.3. int() int () приводит значение к типу int. Синтаксис: int(x) ; где х — переменная любого типа. 5.4.4.4. longO long () приводит значение к типу long. Синтаксис: long(x); где х — переменная любого типа. 5.4.4.5. floatO float () приводит значение к типу float. Синтаксис: long(x); где х — переменная любого типа.
64 Часть II. Среды разработки и язык программирования плат Arduino 5.5. Функции 5.5.1. Цифровой ввод/вывод Рассмотрим функции цифрового ввода/вывода: ? pinMode (); ? digitaiwriteО; ? digitalRead(). 5.5.1.1. Функция pinMode Устанавливает режим работы заданного входа/выхода (pin) как входа или как выхода. Синтаксис: pinMode(pin, mdde); Параметры: ? pin — номер входа/выхода (pin), который вы хотите установить; ? mode — режим. Одно из двух значений: input или output, устанавливается на вход или выход соответственно. Пример: int ledPin = 13; // Светодиод, подключенный к входу/выходу 13 void setup() { pinMode(ledPin, OUTPUT); // устанавливает режим работы - выход 5.5.1.2. Функция digitalWriteQ Подает high или low значение на цифровой вход/выход (pin). Если вход/выход (pin) был установлен в режим выход (output) функцией pinMode (), то для значения high напряжение на соответствующем входе/выходе (pin) будет 5 В C,3 В для плат 3,3 В) и О В (земля) для low. Если вход/выход (pin) был установлен в режим вход (input), to функция digitaiwrite со значением high будет активировать внутренний нагрузочный резистор 20 кОм. Подача low, в свою очередь, отключает этот резистор. Нагрузочного резистора достаточно, чтобы светодиод, подключенный ко входу, светил тускло. Если вдруг светодиод работает, но очень тускло, возможно, необходимо установить режим ВЫХОД (OUTPUT) функцией pinMode (). Синтаксис: digitaiwrite(pin, value);
Глава 5. Программирование плат Arduino Параметры: ? pin — номер входа/выхода (pin); ? value — значение high или low. Пример представлен в листинге 5.19. int ledPin =13; // Светодиод, подключенный ко входу/выходу 13 void setup() { pinMode(ledPin, OUTPUT); // устанавливает режим работы - выход } void loop() { digitalWrite(ledPin:, HIGH); // включает светодиод delayA000); // ждет секунду digitalWrite(ledPin, LOW); // выключает светодиод delay(lOOO); // ждет секунду } ' 5.5.1.3. Функция digitalReadQ Функция считывает значение с заданного входа: high или low. Синтаксис: digitalRead(pin) ; Параметр: pin — номер входа/выхода (pin), который вы хотите считать. Пример представлен в листинге 5.20. int ledPin =13; // Светодиод, подключенный ко входу/выходу 13 int inPin =7; // кнопка на входе 7 int val = 0; // переменная для хранения значения void setup() { pinMode(ledPin, OUTPUT); // устанавливает режим работы - выход для 13 pinMode(inPin, INPUT); // устанавливает режим работы - вход для 7 } void loop () { val = digitalRead(inPin); // считываем значение со входа digitalWrite(ledPin, val); // устанавливаем значение на светодиоде // равным значению входа кнопки
Часть II. Среды разработки и язык программирования плат Arduino Замечание Если вход не подключен, то digitaiRead может возвращать значения high или low случайным образом. Аналоговые входы (analog pins) могут быть использованы как цифровые входы/выходы (digital pins). Обращение к ним идет по номерам от 14 (для аналогового входа 0) до 19 (для аналогового входа 5). 5.5.2. Аналоговый ввод/вывод Рассмотрим функции аналогового ввода/вывода: ? analogRead(); ? analogReference(); О analogWrite(). 5.5.2.1. Функция analogReadf) Функция считывает значение с указанного аналогового входа. Большинство плат Arduino имеют 6 каналов (8 каналов у плат Mini и Nano, 16 — у Mega) с 10-битным аналого-цифровым преобразователем (АЦП). Напряжение, поданное на аналоговый вход (обычно от 0 до 5 В), будет преобразовано в значение от 0 до 1023 — это 1024 шага с разрешением 0,0049 В. Разброс напряжения и шаг может быть изменен функцией analogRef erence о. Считывание значения с аналогового входа занимает примерно 100 мкс @,0001 с), т. е. максимальная частота считывания приблизительно 10 000 раз в секунду. Синтаксис: analogRead(pin); Параметр: pin — номер порта аналогового входа, с которого будет производиться считывание: 0..5 для большинства плат, 0..7 для Mini и Nano и 0..15 для Mega. Возвращаемое значение int @ to 1023). Замечание Если аналоговый вход не подключен, то значения, возвращаемые функцией analogRead (), могут принимать случайные значения. Пример представлен в листинге 5.21. int analogPin =3; // номер порта, к которому подключен потенциометр int val =0; // переменная для хранения считываемого значения void- setup () { Serial.begin(9600); // установка связи, по serial
Глава 5. Программирование плат Arduino 67 void loop() { val = analogRead(analogPin); // считываем значение Serial.println(val); // выводим полученное значение } 5.5.2.2. Функция analogReferenceQ Функция определяет опорное напряжение, относительно которого происходят аналоговые измерения. Функция anaiogReado возвращает значение с разрешением 8 битов A024) пропорционально входному напряжению на аналоговом входе и в зависимости от опорного напряжения. Возможные настройки: ? default — стандартное опорное напряжение 5 В (на платформах с напряжением питания 5 В) или 3,3 В (на платформах с напряжением питания 3,3 В); ? internal— встроенное опорное напряжение 1,1 В на микроконтроллерах ATmegal68 и ATmega328 и 2,56 В на ATmega8; ? external — внешний источник опорного напряжения, подключенный к выводу AREF. Синтаксис: analogReference(type); Параметр: type — определяет используемое опорное напряжение (default, internal или external). Внешнее напряжение рекомендуется подключать к выводу AREF через резистор 5 кОм. Рекомендуемой настройкой для вывода AREF является external. При этом происходит отключение обоих внутренних источников, и внешнее напряжение будет * являться опорным для АЦП. 5.5.2.3. Функция analogWriteQ Выдает аналоговую величину (ШИМ-волну) на порт входа/выхода. Функция может быть полезна для управления яркостью подключенного светодиода или скоростью вращения электродвигателя. После вызова anaiogwrite () на выходе будет генерироваться постоянная прямоугольная «волна» с заданной шириной импульса до следующего вызова analogWrite (ИЛИ вызова digitalWrite, ИЛИ digitalRead на ТОМ же порту входа/выхода). Частота ШИМ-сигнала приблизительно 490 Гц. На большинстве плат Arduino (на базе микроконтроллера ATmegal68 или ATmega328) ШИМ поддерживают порты 3, 5, 6, 9, 10 и 11, на плате Arduino Mega — порты с 2 по 13. На более ранних версиях плат Arduino anaiogwrite () работает только на портах 9, 10 и 11. Для вызова anaiogwrite о нет необходимости устанавливать тип входа/выхода функцией pinMode (). Функция anaiogwrite () никак не связана с аналоговыми входами И С функцией analogRead ().
(№ Часть II. Среды разработки и язык программирования плат Arduino Синтаксис: analogWrite(pin, value); Параметры: ? pin — порт входа/выхода, на который подается ШИМ-сигнал; ? value — период рабочего цикла: значение между 0 (полностью выключено) и 255 (сигнал подан постоянно). Замечание Период ШИМ-сигнала на портах входа/выхода 5 и 6 будет несколько длиннее. Это связано с тем, что таймер для этих выходов также задействован функциями miliis () и delay (). Такой эффект более заметен при установке коротких периодов ШИМ- сигнала @-10). Пример задания яркости светодиода пропорционально значению, снимаемому с потенциометра, представлен в листинге 5.22. int ledPin =9; // Светодиод подключен к выходу 9 int analogPin =3; // потенциометр подключен к выходу 3 int val =0; // переменная для хранения значения void setup() { pinModededPin, OUTPUT); // установка порта на выход } void loop () { val = analogRead(analogPin); // считываем значение с порта, // подключенного к потенциометру analogWrite(ledPin, val / 4); // analogRead возвращает значения от 0 // до 1023f analogWrite должно быть //в диапазоне от 0 до 255 5.5.3. Дополнительные фунции ввода/вывода 5.5.3.1. Функция toneQ Генерирует на порту входа/выхода сигнал— прямоугольную «волну» заданной частоты и с 50%-ным рабочим циклом. Длительность может быть задана параметром, в противном случае сигнал генерируется до тех пор, пока не будет вызвана функция потопе (). К порту входа/выхода может быть подключен пьезо- или иной динамик для воспроизведения сигнала. Воспроизводиться одновременно может только один сигнал. Если сигнал уже воспроизводится на одном порту, то вызов tone () с номером другого порта в качестве
Глава 5. Программирование плат Arduino параметра ни к чему не приведет, если же tone (} будет вызвана с тем же номером порта, то будет установлена новая частота сигнала. Использование функции tone о помешает использовать ШИМ на портах входа/выхода 3 и 11 (кроме платы Arduino Mega). Синтаксис: tone(pin, frequency); tone(pin, frequency, duration); Параметры: ? pin — номер порта входа/выхода, на котором будет генерироваться сигнал; ? frequency — частота сигнала в герцах; ? duration — длительность сигнала в миллисекундах. 5.5.3.2. Функция noTonef) Останавливает сигнал, генерируемый на порту входа/выхода, вызовом функции tone (). Если сигнал не генерировался, то вызов потопе о ни к чему не приводит. Замечание Если необходимы сигналы на разных портах, то следует сначала остановить один сигнал функцией потопе (), а лишь затем создавать новый сигнал на другом порту функцией топе (). Синтаксис: поТопе (pin) ; Параметр: pin — номер порта входа/выхода, на котором прекращается сигнал. 5.5.3.3. Функция shiftOutQ Выводит байт информации на порт входа/выхода последовательно (побитно). Вывод может осуществляться как с первого (левого), так и с последнего (правого) бита. Каждый бит последовательно подается на заданный порт, после чего на синхронизирующий порт входа/выхода подается сигнал, информирующий о доступности к считыванию бита. Такой способ передачи данных называется последовательным протоколом с синхронизацией. Он часто используется для взаимодействия микроконтроллеров с датчиками и сенсорами, а также другими микроконтроллерами. Последовательная передача с синхронизацией позволяет устройствам связываться на максимальной скорости. Смотрите также документацию (на англ. языке) по протоколу последовательного периферийного интерфейса (SPI, Serial Peripheral Interface Protocol). Синтаксис: shiftout(dataPin, clockPin, bitOrder, value);
70^ Часть II. Среды разработки и язык программирования плат Arduino Параметры: ? dataPin — номер порта входа/выхода, на который выводятся биты (int); ? clockPin — номер порта, по которому производится синхронизация (int); ? bitorder— используемая последовательность вывода битов, msbfirst (Most Significant Bit First) — слева или lsbfirst (Least Significant Bit First) — справа; ? value — значение (байт) для вывода (byte). Замечание Порт вывода (dataPin) и синхронизирующий порт (clockPin) должны быть предварительно сконфигурированы как порты вывода с помощью функции pinMode (). Текущая реализация функции sniftouto может выводить только один байт (8 битов) информации, поэтому необходимо произвести несколько действий, чтобы вывести значения больше 255. Пример вывода приведен в листинге 5.23. // Вывод будет MSBFIRST с первого (левого) бита int data = 500; // выводим старший байт shiftOut(dataPin, clock, MSBFIRST, (data » 8)); * % // выводим младший бит shiftOut(dataPin, clock, LSBFIRST, data); // выводим старший бит shiftOut(dataPin, clock, LSBFIRST, (data » 8)); Пример вывода счетчика от 0 до 255 на сдвиговый регистр с последовательным вводом 74НС595 представлен в листинге 5.24. // Порт, подключенный к ST_CP 74HC595 int latchPin = 8; // Порт, подключенный к SH_CP 74HC595 int clockPin = 12; // Порт, подключенный к DS 74HC595 int dataPin = 11; void setup() { // устанавливаем режим порта выхода pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT);
Глава 5. Программирование плат Arduino 71_ void loop () { for (int j = 0; j < 256; j++) { // устанавливаем LOW на latchPin, пока не окончена передача байта digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, LSBFIRST, j); // устанавливаем HIGH на latchPin, чтобы проинформировать регистр, что // передача окончена. digitalWrite(latchPin, HIGH); delayA000); 5.5.3.4. Функция pulselnQ Считывает длину сигнала на заданном порту (high или low). Например, если задано считывание high функцией puisein (), функция ожидает, пока на заданном порту не появится high. Когда high получен, включается таймер, который будет остановлен, когда на порту входа/выхода будет low. Функция puiseino возвращает длину сигнала в микросекундах. Функция возвращает 0, если в течение заданного времени (тайм-аута) не был зафиксирован сигнал на порту. Возможны некоторые погрешности в измерении длинных сигналов. Функция может измерять сигналы длиной от 1.0 мкс до 3 мин. Синтаксис: pulseln(pin, value); pulseln(pin, value, timeout); Параметры: ? pin — номер порта входа/выхода, на котором будет ожидаться сигнал; ? value — тип ожидаемого сигнала: high или low; ? timeout — время ожидания сигнала (тайм-аут) в секундах (unsigned long). Возвращаемые значения: длина сигнала в микросекундах или 0, если сигнал не получен до истечения тайм-аута (тип unsigned long). Пример использования функции представлен в листинге 5.25. int pin = 7; unsigned long duration; void setup() { pinMode(pin, INPUT);
72^ Часть II. Среды разработки и язык программирования плат Arduino void loop() { duration = pulseln(pin, HIGH); 5.5.4. Работа со временем 5.5.4.1. Функция millisQ Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается* на ноль вследствие переполнения значения приблизительно через 50 дней. Параметров нет. Возвращаемое значение — количество миллисекунд с момента начала выполнения программы (ТИП unsigned long). Пример использования функции представлен в листинге 5.26. unsigned long time; void setup() { Serial.begin(9600); } void loopO { Serial.print("Time: "); time = millisO; // выводит количество миллисекунд с момента начала выполнения программы Serial.println(time); // ждет секунду перед следующей итерацией цикла. delayA000); 5.5.4.2. Функция microsQ Возвращает количество микросекунд с момента начала выполнения на плате Arduino текущей программы. Значение переполняется и сбрасывается на ноль приблизительно через 70 мин. На платах Arduino с 16 МГц (Duemilanove и Nano) функция micros () имеет разрешение 4 с (возвращаемое значение всегда кратно 4). На платах с 8 МГц (Arduino Lilypad) разрешение функции — 8 с. Параметров нет. Возвращаемое значение — количество микросекунд с момента начала выполнения программы (unsigned long).
Глава 5. Программирование плат Arduino 73_ Пример использования функции представлен в листинге 5.27. unsigned long time; void setup () { Serial.begin(9600); } void loop() { Serial.print("Time: "); time = micros(); // выводит количество микросекунд с момента начала выполнения // программы Serial.printIn(time); // ждет секунду перед следующей итерацией цикла. delayA000); 5.5.4.3. Функция delayO Останавливает выполнение программы на заданное в параметре количество миллисекунд A000 мс в 1 с). Синтаксис: delay (ms); Параметр: ms — количество миллисекунд, на которое приостанавливается выполнение программы ( ТИП unsigned long). Пример использования функции представлен в листинге 5.28. int ledPin = 13; // светодиод подключен на порт 13 void setup() { pinMode(ledPin, OUTPUT); // устанавливается режим порта - выход } void loop() { digitalWrite(ledPin, HIGH); // включаем светодиод delayA000); // ожидаем секунду digitalWrite(ledPin, LOW); // выключаем светодиод delayA000); // ожидаем секунду
74 Часть II. Среды разработки и язык программирования плат Arduino Не рекомендуется использовать эту функцию для событий длиннее 10 мс, т. к. во время останова не могут быть произведены манипуляции с портами, не могут быть считаны сенсоры или произведены математические операции. В качестве альтернативного подхода возможно контролирование времени выполнения тех или иных функций с помощью minis (). При использовании функции delay () работа прерываний не останавливается, продолжается запись последовательно (serial) передаваемых данных на RX-порт, ШИМ-сигнал (anaiogwrite) на портах продолжает генерироваться. 5.5.4.4. Функция delayMicrosecondsQ Останавливает выполнение программы на заданное в параметре количество микросекунд A 000 000 мкс в 1 с). В существующих версиях Arduino максимальная пауза, воспроизводимая корректно,— 16 383. Возможно, это будет изменено в следующих версиях Arduino. Для остановки выполнения программы более чем на несколько тысяч микросекунд рекомендуется использовать функцию delay (). Синтаксис: delayMicroseconds(us); Параметр: us — количество Микросекунд, на которое приостанавливается выполнение программы (unsigned int). Пример использования функции представлен в листинге 5.29. int outPin =8; // цифровой порт входа/выхода 8 void setup() { pinMode(outPin, OUTPUT); // устанавливается режим порта - выход } void loop () { digitalWrite(outPin, HIGH); // подаем HIGH на выход delayE0); // ожидаем 50 мкс digitalWrite(outPin, LOW); // устанавливаем LOW на выходе delayE0); // ожидаем 50 мкс 5.5.5. Математические функции В языке представлены следующие математические функции: min () ? max () ? abs (), constrain (), map (), pow (), sq (), sqrt ().'
Глава 5. Программирование плат Arduino 75^ 5.5.5.1. Функция min(x,yx) Возвращает наименьшее из двух значений. Параметры: ? х — первое число, любой тип; ? у — второе число, любой тип. Возвращаемое значение — возвращает меньшее из двух сравниваемых значений. Пример использования функции: sensVal = min(sensVal, 100); // проверяем, если sensVal больше 100, то senseVal будет присвоено 100 5.5.5.2. Функция тах(х, у) Возвращает большее из двух значений. Параметры: ? х — первое число, любой тип; ? у — второе число, любой тип. Возвращаемое значение — возвращает большее из двух сравниваемых значений. Пример использования функции: sensVal = max(sensVal, 20); // проверяем, если sensVal меньше 20, то senseVal будет присвоено 20 Функция max о зачастую используется для задания нижней границы значений переменной. Функцией min () ограничивают верхнюю границу переменной. В силу специфики реализации функции шах о следует избегать использования других функций в качестве параметров. Например: max(а—, 0); // может привести к некорректным результатам а—; max(а, 0); // так корректно 5.5.5.3. Функция abs() Возвращает модуль числа. Параметр: х — число. Возвращаемые значения: ? х — если х больше или равен 0; ? -х — если х меньше 0. В силу специфики реализации функции abs () следует избегать использования других функций в качестве параметров: abs (а++); // может привести к некорректным результатам а++; abs(а, 0); // так корректно
76^ Часть II. Среды разработки и язык программирования плат Arduino 5.5.5.4. Функция constrainfx, a, Ь) Функция проверяет и, если надо, задает новое значение так, чтобы оно было в области допустимых значений, заданной параметрами. Параметры: ? х — проверяемое значение, любой тип; ? а — нижняя граница области допустимых значений, любой тип; ? ь — верхняя граница области допустимых значений, любой тип. Возвращаемое значение: Ох — если х входит в область допустимых значений [а. .ь]; ? а — если х меньше а; ? ь — если х больше ь. Пример: sensVal * constrain(sensVal, 10, 150); // ограничиваем значения sensVal диапазоном от 10 до 150 5.5.5.5. Функция mapfvalue, fromLow, fromHigh, toLow, toHigh) Функция пропорционально переносит значение (value) из текущего диапазона значений (fromLow ... fromHigh) в новый диапазон (toLow ... toHigh), заданный параметрами. Функция шар () не ограничивает значение рамками диапазона, как это делает функция constrain (). Функция constrain () может быть использована до или после вызова шар (), если необходимо ограничить допустимые значения заданным диапазоном. Обратите внимание, что «нижняя граница» может быть как меньше, так и больше «верхней границы». Это может быть использовано, чтобы «перевернуть» диапазон: у - тар(х, 1, 50, 50, 1); Возможно использование отрицательных значений: у - тар(х, 1, 50, 50, -100); Функция тар о оперирует целыми числами. При пропорциональном переносе дробная часть не округляется по правилам, а просто отбрасывается. Параметры: ? value — значение для переноса; ? fromLow — нижняя граница текущего диапазона; ? fromHigh — верхняя граница текущего диапазона; ? toLow — нижняя граница нового диапазона, в который переносится значение; ? toHigh — верхняя граница нового диапазона. Возвращаемое значение — значение в новом диапазоне.
Глава 5. Программирование плат Arduino 77_ Пример использования функции представлен в листинге 5.30. // Переносим значение с аналогового входа // (возможные значения от 0 до 1023) в 8 бит @..255) void setup() {;} void loop () { int val = analogRead(O); val = map(val, 0, 1023, 0, 255); analogWrite(9, val); 5.5.5.6. Функция powfbase, exponent) Вычисляет значение, возведенное в заданную степень. Функция pow () может возводить и в дробную степень. Параметры: ? base — ЧИСЛО (ТИП float); ?I exponent — степень, в которую будет возводиться число (тип float). Возвращаемое значение — результат возведения в степень, число (тип double). 5.5.5.7. Функция sq(x) Функция возвращает квадрат числа, заданного параметром. Параметр: х — число, любой тип. Возвращаемое значение — квадрат числа. 5.5.5.8. Функция sqrt(x) Функция вычисляет квадратный корень числа, заданного параметром. Параметры: х — число, любой тип. Возвращаемое значение — квадратный корень числа (тип double). 5.5.6. Тригонометрические функции В языке представлены следующие тригонометрические функции: ? sin(); ? cos о; ? tan ().
78^ Часть II. Среды разработки и язык программирования плат Arduino 5.5.6.1. Функция sin(rad) Возвращает синус угла, заданного в радианах в передаваемом параметре. Результат функции всегда в диапазоне -1... 1. Параметр: rad — угол в радианах (float). Возвращаемое значение: синус угла (тип double). 5.5.6.2. Функция cos(rad) Возвращает косинус угла, заданного в радианах в передаваемом параметре. Результат функции всегда находится в диапазоне -1... 1. Параметр: rad — угол в радианах (тип float). Возвращаемое значение: косинус угла (тип double). 5.5.6.3. Функция tan(rad) Возвращает тангенс угла, заданного в радианах в передаваемом параметре. Результат функции в диапазоне от минус бесконечности до плюс бесконечности. Параметр: rad — угол в радианах (тип float). Возвращаемое значение: тангенс угла (тип double). 5.5.7. Генераторы случайных значений Функции формирования случайных чисел: ? randomSeed(); ? random (). 5.5.7.1. Функция randomSeed(seed) Функция randomSeed о инициализирует генератор псевдослучайных чисел. Генерируемая последовательность случайных чисел очень длинная, и всегда одна и та же. Точка в этой последовательности, с которой начинается генерация чисел, зависит от параметра seed. Параметр: seed — параметр, задающий начало выдачи псевдослучайных значений на последовательности (тип int, long). 5.5.7.2. Функция randomQ Функция random () возвращает псевдослучайное число. Синтаксис: random (max); random(min, max);
Глава 5. Программирование плат Arduino 79_ Параметры: ? min — нижняя граница случайных значений, включительно (опционально); ? шах — верхняя граница случайных значений, включительно. Возвращаемое значение: случайное число между min и max - 1 (тип long). Если при каждом запуске программы необходимо получать разные последовательности значений, генерируемых функцией randomo, то необходимо инициализировать генератор псевдослучайных чисел со случайным параметром. Например, можно использовать значение, отдаваемое функцией anaiogReadO с неподключенного порта входа/выхода. В некоторых случаях необходимо получать одинаковую последовательность при каждом запуске программы на Arduino. Тогда инициализировать генератор псевдослучайных чисел следует вызовом функции randomseed () с фиксированным параметром. Пример использования функции представлен в листинге 5.31. long randNumber; void setup() { Serial.begin(9600); } void loop() { // выводим случайное число из диапазона 0..299 randNumber = randomC00); Serial.println(randNumber); // выводим случайное число из диапазона 0..19 randNumber = randomA0, 20); Serial.println(randNumber); delayE0); 5.5.8. Операции с битами и байтами Функции — операции с битами и байтами: lowByte(), highByte(), bitRead(), bitWrite(), bitSet(), bitClear(), bit(). 5.5.8.1. Функция lowByteQ Извлекает младший (самый правый) байт переменной (например, типа word). Синтаксис: lowByte(x); Параметр: х — величина любого типа. Возвращает байт.
8? Часть II. Среды разработки и язык программирования плат Arduino 5.5.8.2. Функция highByteQ Извлекает старший (крайний левый) байт слова (или второй младший байт большего типа данных). Синтаксис: highByte(x); Параметр: х — величина любого типа. Возвращает байт. 5.5.8.3. Функция bitReadQ Читает определенный бит переменной. Синтаксис: bitRead<x, n); Параметры: ? х — число, из которого необходимо прочитать; ? п — указывает бит, который необходимо прочитать, начиная с 0 для младшего (правого) бита. Возвращает: значение бита @ или 1). 5.5.8.4. Функция bitWriteQ Записывает бит числовой переменной. Синтаксис: bitWrite(x, n, b) ; Параметры: ? х — числовая переменная, в которую необходимо записать; ? п — номер бита, который необходимо записать, начиная с 0 для младшего (левого) бита; П ь — значение, которое необходимо записать в бит @ или 1). 5.5.8.5. Функция bitSetf) Устанавливает (записывает 1) бит числовой переменной. Синтаксис: bitSet(x, n) Параметры: ? х — числовая переменная, которую необходимо записать; ? п— номер бита, который необходимо установить, начиная с 0 для младшего (левого) бита.
Глава 5. Программирование плат Arduino 81_ 5.5.8.6. Функция bitClearQ Сбрасывает (записывает 0) бит числовой переменной. Синтаксис: bitClear(x, п); Параметры: ? х — числовая переменная, которую необходимо записать; On— номер бита, который необходимо установить, начиная с 0 для младшего (левого) бита. 5.5.8.7. Функция bit() Вычисляет значение указанного бита (бит 0 — это 1, бит 1 — это 2, бит 2 — это 4 и т. д.). Синтаксис: bit (n) ; Параметр: п — номер бита, который необходимо вычислить. Возвращает: значение бита. 5.5.9. Внешние прерывания Прерывание (англ. interrupt)— сигнал, сообщающий процессору о наступлении какого-либо события. При этом выполнение текущей последовательности команд приостанавливается и управление передается обработчику прерывания, который выполняет работу по обработке события и возвращает управление в прерванный код. Arduino также предоставляет свои функции для работы с прерываниями. Их всего две: П attachlnterrupt(); ? detachlnterrupt(). 5.5.9.1. Функция attachlnterrupt Задает функцию обработки внешнего прерывания, т. е. функцию, которая будет вызвана по внешнему прерыванию. Если до этого была задана другая функция, то назначается новая. Вычисляет значение указанного бита (бит 0— это 1, бит 1 — это 2, бит 2 — это 4 и т. д.). Синтаксис: attachlnterrupt(interrupt, function, mode); Параметры: ? interrupt — номер прерывания: • о — на цифровом порту 2; • 1 — на цифровом порту 3;
82 Часть II. Среды разработки и язык программирования плат Arduino • 2 — на цифровом порту 21 (для Arduino Mega); • з — на цифровом порту 21 (для Arduino Mega); • 4 — на цифровом порту 21 (для Arduino Mega); • 5 — на цифровом порту 21 (для Arduino Mega); ? function— функция, вызываемая прерыванием (должна быть без параметров и не возвращать значений); ? mode— задает режим обработки прерывания, допустимо использование следующих констанст: • low — вызывает прерывание, когда на порту low; • change — прерывание вызывается при смене значения на порту с low на high и наоборот; • rising — прерывание вызывается только при смене значения на порту с low на high; • falling — прерывание вызывается только при смене значения на порту с high на low. Возвращаемого значения нет. Внутри функции обработки прерывания не работает функция delay о, значения, возвращаемые функцией minis о, не изменяются. Возможна потеря данных, передаваемых по последовательному соединению (Serial data) в момент выполнения функции обработки прерывания. Переменные, изменяемые в функции, должны быть объявлены как volatile. 5.5.9.2. Функция detachlnterrupt Выключает обработку внешнего прерывания. Синтаксис: detachlnterrupt(interrupt); Параметр: interrupt — номер прерывания @ или 1), для Arduino Mega еще 2, 3, 4 или 5. Возвращаемого значения нет. В листинге S.32 приведен пример использования прерывания 0 при наступлении события change на порту 2. При этом светодиод на выводе 13 Arduino при каждом прерывании меняет статус (горит либо гаснет). int pin = 13; volatile int state - LOW;
Глава 5. Программирование плат Arduino 83 void setup() pinMode(pin, OUTPUT); attachInterrupt(O, blink, CHANGE); } void loopO digitalWrite(pin, state); // функция обработки прерывания void blink() state = !state; 5.6. Управление портами через регистры ATmega Использование встроенных функций Arduino для работы с выводами очень удобно. Однако за удобство приходится платить быстродействием. В большинстве программ это не принципиально, но иногда имеет решающее значение. В таких случаях необходимо управлять портами Arduino напрямую через регистры ATmega. При этом мы не только увеличиваем быстродействие, но еще и уменьшаем размер программы. Рассмотрим таблицу соответствия выводов Arduino портам микроконтроллера ATmega 168 (рис. 5.2). Что мы видим: ? portd — цифровые выводы Arduino от D0-D7; ? portb — цифровые выводы Arduino от D8-D13; ? portc — аналоговые выводы Arduino A0-A5 (А6-А7 доступны в Arduino Mini, Nano). Для установки выводов и чтения записи данных используются следующие команды: ? ddrd, ddrb, ddrc — для установки назначения выводов (направления передачи данных) соответствующих портов @ — input, 1 — output); ? portd, portb, portc — регистр установки данных на выводах соответствующего порта; ? pind, pinb, pinc — считывание данных всех контактов соответствующего порта. Используя эти команды, можно заменить стандартные функции Arduino для управления выводами. Например:
84 Часть II. Среды разработки и язык программирования плат Arduino // pinModeA3,INPUT) DDRB = DDRB & B11011111 // pinModeE,OUTPUT) DDRD = DDRD | B00100000 // digitalWriteG,HIGH) PORTB= PORTB | B10000000 // digitalReadA0,HIGH) PINB = (PINB & B00000100)»2 Arduino: сброс цифровой вывод 0 (RX) цифровой вывод 1(ТХ) цифровой вывод 2 цифровой вывод 3 (PWM) цифровой вывод 4 VCC OND Кварцевый резонатор Кварцевый резонатор цифровой вывод 5 (PWM) цифровой вывод 6 (PWM) цифровой вывод 7 цифровой вывод 8 Atnwgai68 Pin Mapping (PCIMTIpVRXO)POOC <PONT16V1NTO)P02C (рс^пвдхгелкп) роз с (PCINT20/XCK/TO)P04C vocc GNDC (PCtNT67XTAl1/TOSC1) РввС (PC«NT7/XTAl2m>SC2)PB7C ^Рм№|Т|НЮ0|Шм1| РО5С. fPQMT22yOCOA/AINOi РОв?! (PC1KT23/AIN1)PO7C (РС1НТ0ЛХКОЯСР1)РВ0С rv- 1 1 i 1 § г 1 I il it *t «1 M ш m m m m ш m * ш n ft fl If ЗPC4(AOC^SOA^a^Л¦12) 3143 (AOC3^CIHT11) DPC2(A0C2FC!NTtO) ЗРС1(АОС1Л»С1НТ9) 3PCO(AOC(VPCtMTS) 3Af«F 3JM0C 3Pe5<SCK/PaKT5) 3P84(KKSOAaCINT4) ЗРвг<88ЮС1»РС1НТ2) Цифровые выводы 11,12 и 13 (выводы одкроосемы Atmega168 Ns 17. 18 и 19) используются портом CSP дли подключения MISO, MOSI, SCK . Избегайте низкоомной нагрузки на этих выводах при использовании портом CSP Arduino: аналоговый вывод 5 аналоговый вывод 4 аналоговый вывод 3 аналоговый вывод 2 аналоговый вывод 1 аналоговый вывод 0 GNO аналоговый опорный сигнал VCC цифровой вывод 13 цифровой вывод 12 цифровой вывод 11 (PWM) цифровой вывод 10 (PWM) цифровой вывод 9 (PWM) Рис. 5.2. Соответствие выводов Arduino портам микроконтроллера АТтеда Команда anaiogwrite () выдает ШИМ-волну на порт входа/выхода. Частота ШИМ- сигнала приблизительно 490 Гц. Если вам хочется иметь ШИМ с большим разрешением или другой частотой, чем позволяет стандартная функция anaiogwrite о, или вы желаете организовать ШИМ на других вводах, можно написать замену функции anaiogwrite (), используя прямой вывод на порты: int PWM_tiine=32; for (int k=0;k<PWM_time) PORTA=B00000001; for (int k=0;k<256-PWM_time) PORTA=B00000000; Теперь можно переходить к изучению Arduino на практических примерах. Этому будут посвящены остальные главы книги.
ЧАСТЬ III Практическое применение Arduino Глава 6. Проекты для изучения выводов Arduino Глава 7. Использование библиотек в проектах Arduino Глава 8. Arduino и последовательный порт UART Глава 9. Подключение датчиков к плате Arduino Глава 10. Использование дисплеев в проектах Arduino Глава 11. Подключение к Arduino исполнительных устройств Глава 12. Arduino и беспроводная связь Глава 13. Arduino и Интернет вещей Глава 14. RFID-идентификация Глава 15. Специальные возможности отдельных плат Arduino Глава 16. Взаимодействие Arduino с другими программируемыми системами Глава 17. Программирование в среде Arduino IDE других плат
ГЛАВА 6 Проекты для изучения выводов Arduino Платы Arduino позволяют использовать большую часть своих контактов ввода/вывода для связи с устройствами во внешних схемах. Прежде всего, эти контакты могут служить цифровыми вводами и выводами. В то же время часть контактов Arduino могут действовать и как аналоговые входы. Многие из имеющихся контактов, кроме того, также мультиплексированы — способны выступать в роли различных коммуникационных интерфейсов, последовательных интерфейсов, широтно- импульсных модуляторов и обработчиков внешних прерываний. В этой главе мы рассмотрим использование контактов Arduino на примере простых и не очень проектов. 6.1. Цифровые выводы — «бегущий огонь» на светодиодах Первое, с чего можно начать знакомство с платформой Arduino, — это ее цифровые выводы. Цифровые выводы, имеющиеся на платах Arduino, позволяют подключать к ним другие микросхемы, а также различные датчики и приводы. Изучение возможностей использования цифровых выводов Arduino позволит вам организовать с ее помощью решение многих практически полезных задач. Цифровые сигналы имеют только два отдельных значения: высокий (high, 1) и низкий (low, 0) уровни. Вы можете задействовать цифровые сигналы в случаях, когда вход или выход будут принимать одно из этих двух значений. Например, одной из ситуаций, в которых вы можете использовать цифровой сигнал, является включение и выключение светодиода. Поскольку цифровые выводы Arduino могут выступать в роли как входов, так и выходов, сначала необходимо их настроить. Для настройки цифровых выводов в Arduino используется встроенная функция pimMode (), которая имеет следующий синтаксис: pinMode (pin, mode)
Часть III. Практическое применение Arduino где: ? pin — номер вывода Arduino; ? mode — устанавливаемый режим для вывода pin: • input — pin в режиме входа; • output — pin в режиме выхода; • inputpullup — в этом режиме к выводу подключается внутренний подтягивающий резистор 20 кОм, чтобы привести уровень на выводе к значению HIGH, если к нему ничего не подключено. 6.1.1. Подключение светодиода к выводу Arduino Рассмотрим простейший пример— подключение к выводу Arduino светодиода. Для него нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования (монтажная плата); ? красный светодиод; ? резистор 220 Ом; ? соединительные провода типа «папа-папа» (далее в тексте — ПП) — 3 шт. Рис. 6.1. Монтажная схема подключения светодиода В представленной на рис. 6Л монтажной схеме этого проекта для подключения светодиода к цифровому выводу мы применили ограничительный резистор номиналом 220 Ом. Разберемся, как подобрать такой резистор, и как будет влиять номинал резистора на яркость светодиода. Самым главным уравнением для любого
Глава 6. Проекты для изучения выводов Arduino 89^ инженера-электрика является закон Ома— он определяет отношения между напряжением, током и сопротивлением в цепи. Закон Ома записывается следующим образом: где: V— напряжение в вольтах, /— ток в амперах, R — сопротивление в омах. В электрической схеме каждый компонент имеет какое-то собственное сопротивление, что вызывает при прохождении через него тока некоторое снижение его напряжения, называемое падением напряжения. Светодиоды также вызывают определенное падение напряжения на них и предназначены для работы при определенном значении тока: чем больше ток через светодиод, тем ярче светодиод светится, и так до предельного значения. Для наиболее распространенных типов светодиодов максимальный ток составляет 20 мА. Обычное значение падения напряжения для светодиода— около 2 В. Учитывая, что напряжение питания, равное 5 В, должно упасть на светодиоде и резисторе, оставшиеся 3 В должны упасть на резисторе. Зная максимальное значение прямого тока через светодиод B0 мА), можно найти номинал резистора: R=V/I= 3/0,02 =150 Ом. Таким образом, при протекании тока величиной 20 мА через светодиод и резистор сопротивлением 150 Ом светодиод станет светиться на полную мощность, а по мере увеличения значения сопротивления ток будет уменьшаться и свечение светодиода снижаться. И хотя сопротивление взятого нами в проект резистора B20 Ом) превышает теоретически рассчитанное значение 150 Ом, однако оно все же позволяет светодиоду светиться достаточно ярко. Кроме того, резисторы этого номинала очень распространены. Теперь приступим к написанию программы (скетча), которая обеспечит мигание светодиода, подключенного к цифровому выводу Arduino D5. В процедуре setup () настроим режим работы вывода (пина) 5 на выход (output): void setup() { pinModeE,OUTPUT); } В процедуре loop () нам необходимо зажечь светодиод — подать high A) на цифровой вывод D5, подождать некоторое время, затем потушить его — подать low @) на цифровой вывод D5, опять подождать некоторое время, и т. д. по кругу. Для выполнения операции «подождать некоторое время» мы воспользуемся встроенной в Arduino функцией delay (). Эта функция просто останавливает выполнение программы на заданное в параметре количество миллисекунд (напомню: в 1 с 1000 мс). Например: delayB000); // останавливает выполнение программы на 2000 мс B с) Полный код скетча приведен в листинге 6.1.
90 Часть III. Практическое применение Arduino void setup() { // настроить выводы D5 Arduino как OUTPUT pinModeE, OUTPUT); void loop() { // включить светодиод (установить D5 HIGH) digitalWriteE, HIGH); // пауза 1000 мс A с) delayA000); // выключить светодиод (установить D5 LOW) digitalWriteE, LOW); // пауза 1000 мс A с) delayA000); Загрузим этот скетч в плату Arduino— мы должны наблюдать включение/выключение светодиода с периодичностью 2 с. Допустим, вы хотите изменить вывод (пин) Arduino для подключения светодиода или время паузы в функции delay о. Можно просто внести изменения в каждую строку скетча, содержащую изменяемые параметры. Но это не совсем удобно, и в больших программах займет очень много времени. Есть и другой вариант — использовать константы и переменные. Константа и переменная — это места хранения данных. Они имеют имя, значение и тип. Например, следующее объявление (оно называется декларацией): int pin_ledl = 5; создает переменную с именем piniedi, значением 5 и типом int. После этого мы получаем в программе возможность обратиться к ней следующим образом: const int pin_ledl =5; Декларация: const int pin_led2 = 6; создает константу с именем pin_ied2, значением б и типом int. Константы можно определить и иначе: #define PIN_LED2 6 В этом случае не надо указывать тип переменной. Таким образом, в программе возникает возможность обратиться к той или иной переменной или константе через ее имя с целью работы с ее значением. Например, в утверждении: pinMode(pin_ledl, OUTPUT);
Глава 6. Проекты для изучения выводов Arduino уже подразумевается значение вывода E), которое будет передаваться в функцию pinMode (). Преимущество переменных и констант заключается в том, что определение значения вывода осуществляется однажды, а потом можно использовать его многократно. Константа, в отличие от переменной, не может изменять свое значение — оно предопределено при декларации константы, а значение переменной можно менять в программе. Внесем изменения в предыдущий скетч (см: листинг 6.1), используя константу и переменную, и получим скетч, представленный в листинге 6.2. // константа пина Arduino для подключения светодиода #define PIN_LED 5 // переменная для паузы (тип unsigned long) unsigned long pause=1000; void setup () { // настроить вывод Arduino как OUTPUT pinMode(PIN_LED, OUTPUT); void loopO { // включить светодиод digitalWrite(PIN_LED, HIGH); // пауза delay(pause); // выключить светодиод digitalWrite(PIN_LED, LOW); // пауза delay(pause); Загрузим этот скетч в плату Arduino и убедимся, что он работает. В чем его преимущество перед скетчем из листинга 6.1? Если вам понадобится поменять вывод (пин) для подключения светодиода, то внести изменения надо будет только в одну строку: #define PIN_LED 5 Если понадобится поменять длительность паузы, внести изменения нужно будет тоже только в одну строку: unsigned long pause=1000; Вы можете поэкспериментировать со скетчем, меняя пин подключения светодиода и значение переменной pause, определяющей частоту его мигания.
Часть III. Практическое применение Arduino 6.1.2. Подключение к плате Arduino 8 светодиодов Рассмотрим теперь более сложный пример: подключим к плате Arduino 8 светодиодов и создадим из них «бегущий огонь». Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования (монтажная плата); ? красный светодиод — 8 шт.; ? резистор 220 Ом — 8 шт.; ? соединительные провода ПП — 17 шт. Монтажная схема этого проекта приведена на рис. 6.2. Рис. 6.2. Монтажная схема подключения светодиодов для проекта «бегущий огонь» Приступим к написанию скетча. Прежде всего в процедуре setup о настроим режим работы пинов Arduino, к которым подключены светодиоды, как output (выходы): pinModeA2,OUTPUT); pinModeA1,OUTPUT); pinModeA0/OUTPUT); pinMode(9,OUTPUT);
Глава 6. Проекты для изучения выводов Arduino 93^ pinMode (8, OUTPUT) ; pinModeG,OUTPUT); pinModeF,OUTPUT); pinModeE,OUTPUT); И в начале программы все светодиоды потушены: digitalWriteA2,LOW); digitalWriteA1,LOW); digitalWriteA0,LOW); digitalWrite(9,LOW); digitalWrite(8,LOW); digitalWriteG,LOW); digitalWriteF,LOW); digitalWriteE,LOW); В основном цикле программы — loop () — нам необходимо зажечь первый свето- диод, подождать некоторое время, затем выключить его и зажечь второй светодирд, подождать некоторое время, выключить второй светодиод и зажечь третий, и т. д. по кругу. Для выполнения операции «подождать некоторое время», как и в предыдущем примере, мы воспользуемся встроенной в Arduino функцией delay о. Эта функция просто останавливает выполнение программы на заданное в параметре количество миллисекунд. Соответственно внесем в функцию loop () следующий код: void loop() { digitalWriteA2,HIGH); delayA000); digitalWriteA2,LOW); digitalWriteA1,HIGH); delayA000); digitalWriteA1,LOW); digitalWriteA0,HIGH); delayA000); digitalWriteA0,LOW); digitalWrite(9,HIGH); delayA000); digitalWrite(9,LOW); digitalWrite(8,HIGH); delayA000); digitalWrite(8,LOW); digitalWriteG,HIGH); delayA000); digitalWriteG,LOW); digitalWriteF,HIGH); delayA000); digitalWriteF,LOW); digitalWriteE,HIGH);
94 Часть III. Практическое применение Arduino delayA000); digitalWriteE,LOW); } Полный код получившегося скетча приведен в листинге 6.3. void setup() { // настроить выводы Arduino 12,11,10,9,8,7, 6,5 // как OUTPUT pinModeA2,OUTPUT); pinModeA1,OUTPUT); pinModeA0,OUTPUT); pinMode(9,OUTPUT); pinMode(8,OUTPUT); pinModeG,OUTPUT); pinModeF,OUTPUT); pinModeE,OUTPUT); // все светодиоды потушить digitalWriteA2,LOW); digitalWrite(ll,LOW); digitalWriteA0,LOW); digitalWrite(9,LOW); digitalWrite(8,LOW); digitalWriteG,LOW);' digitalWriteF,LOW); digitalWriteE,LOW); void I6op() { digitalWriteA2,HIGH); delayA000); digitalWriteA2,LOW); digitalWriteA1,HIGH); delayA000); digitalWriteA1,LOW); digitalWriteA0,HIGH); delayA000); digitalWriteA0,LOW); digitalWrite(9,HIGH); delayA000); digitalWrite(9,LOW); digitalWrite(8,HIGH); delayA000) ; digitalWrite(8,LOW); digitalWriteG,HIGH); delayA000);
Глава 6. Проекты для изучения выводов Arduino digitalWriteG,LOW); digitalWriteF,HIGH); delayA000); digitalWriteF,LOW); digitalWriteE,HIGH); delayA000); digitalWriteE,LOW); Загрузим этот скетч в плату Arduino — мы должны наблюдать бегущий по свето- диодам «огонь» сначала в одну сторону, а затем в обратную. Основной недостаток нашего скетча — он слишком громоздкий. И даже объявление каждого вывода с помощью переменных не исправит положения. К счастью, есть способ значительно упростить этот скетч, воспользовавшись массивами. Массив — это набор переменных, доступ к которым осуществляется через их индекс. Рассмотрим варианты создания (объявления) массива: ? объявление массива без его инициализации: int pin_leds[8]; ? можно объявить массив без непосредственного указания размера. Компилятор подсчитает количество элементов и создаст массив соответствующего размера: int pinJLeds t] = {5,6,7,8,9,10,11,12}; ? можно одновременно инициализировать и указать размер создаваемого массива: int pin_leds [8] = {5,6,7,8,9,10,11,12}; ? объявление массива типа char: char message[8] = "Arduino"; Учтите, что в таком случае необходимо предусмотреть еще один элемент для хранения обязательного null-символа: Теперь рассмотрим, как осуществляется доступ к элементам массива. Индексация в массивах начинается с нуля. То есть первый элемент массива будет иметь порядковый номер 0. Таким образом: pin_leds[0] = 5; pin_leds[l] = 6; Как работать с массивами? Очень удобный метод работы с массивами— циклы. При этом счетчик цикла используется для индексации каждого элемента массива. Например, для создания массива с номерами пинов, задаваемыми командой pinMode (), необходимо выполнить следующий код: int i; for (i = 0; i < 8; i = i + 1) { pinMode (pin_leds [ i ], OUTPUT) ;
Часть III. Практическое применение Arduino Внесем соответствующие изменения в предыдущий скетч (см. листинг 6.3), используя массивы, и получим скетч, представленный в листинге 6.4. В нем кроме массивов мы предусмотрели для позиции горящего светодиода переменную pos, которая после каждой паузы станет увеличиваться на 1, а при достижении значения 8 будет обнуляться: pos=(pos+l)%8; При этом мы запускаем цикл for для массива pin_ieds[i] с пинами подключения светодиодов, в котором для всех элементов массива с индексом, не равным значению переменной pos, выполняем команду: digitalWrite(pin_leds[i],LOW); а для элемента массива с индексом, равным значению переменной pos, команду: digitalWrite (pin_leds [i], HIGH); // массив, элементы которого - пины // подключения светодиодов int pin_leds [8] = {12,11,10,9,8,7,6,5}; // позиция горящего светодиода int pos=0; // переменная для паузы (тип unsigned long) unsigned long pause=1000; void setup() { // настроить выводы Arduino как OUTPUT for (int i = 0; i < 8; i = i + 1) { pinMode(pin_leds [i],OUTPUT); } // все светодиоды потушить for (int i = 0; i < 8; i = i + 1) { digitalWrite(pin_leds [i],LOW); void loopO { // все светодиоды потушить for (int i = 0; i < 8; i = i + 1) { // зажечь светодиод, если он =pos if(i==pos) digitalWrite(pin_leds [i],HIGH); // все остальные потушить else digitalWrite (pin_leds [i], LOW) ;
Глава 6. Проекты для изучения выводов Arduino 97 // пауза delay(pause); // изменить позицию горящего светодиода pos=(pos+l)%8; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_04 сопровождающего книгу электронного архива. Напоминаю, что этот электронный архив можно скачать с FTP-сервера издательства «БХВ-Петербург» по ссылке ftp://ftp.bhv.ru/ 9785977567114.zip, а также со страницы книги на сайте www.bhv.ru (см. приложение 2). Загрузим этот скетч в плату Arduino и убедимся в его работоспособности. Метод расположения пинов в пределах массива очень удобен и позволяет переставлять их как угодно без переподключения светодиодов. Вы можете самостоятельно поэкспериментировать с расположением элементов массива. 6.2. Цифровые входы — управляем светодиодами с помощью кнопок Рассмотрим еще одну функцию цифровых выводов. В предыдущем разделе мы использовали их в качестве выходов, генерируя цифровой сигнал для включения или выключения светодиодов. Сейчас мы попробуем сконфигурировать выводы Arduino в качестве входов. Это позволит подключить к Arduino, например, переключатели и кнопки для взаимодействия с внешними устройствами в режиме реального времени. И в этом разделе вы научитесь определять программное действие по нажатию кнопки. Установка режима того или иного вывода (на вход или на выход) осуществляется С ПОМОЩЬЮ фуНКЦИИ pinMode () Г » pinModeB,INPUT); // вывод D2 установлен как выход 6.2.1. Подключение кнопки к плате Arduino Чтобы подключить к Arduino нормально разомкнутую кнопку, можно пойти самым простым путем: один свободный проводник кнопки соединить с питанием или «землей», а другой — с цифровым выводом Arduino (рис. 6.3). Но это неправильно — все то время, когда кнопка не замкнута, на цифровом выводе Arduino могут появляться электромагнитные наводки, и из-за этого возможны ложные срабатывания. Чтобы избежать наводок, цифровой вывод обычно подключают через достаточно большой резистор E-10 кОм) либо к питанию, либо к «земле». В первом случае это называется схемой с подтягивающим резистором (рис. 6.4), во втором — схемой со стягивающим резистором (рис. 6.5). Резисторы в обеих схемах используются для
98 Часть III. Практическое применение Arduino Рис. 6.3. Монтажная схема подключения кнопки к выводу Arduino +5 В Рис. 6.4. Монтажная {вверху) и принципиальная {внизу) схемы подключения кнопки к Arduino: схема с подтягивающим резистором
Глава 6. Проекты для изучения выводов Arduino 99 +5 В ЮкОм Рис. 6.5. Монтажная (вверху) и принципиальная (внизу) схемы подключения кнопки к Arduino: схема со стягивающим резистором установки «значения по умолчанию» из входного контакта: в схеме с подтягивающим резистором это high, в схеме со стягивающим резистором — low. Но вот любопытный момент. Ранее мы утверждали, что схема, показанная на рис. 6.3, неверная, но это только в том случае, если мы используем такой режим установки: pinMode B, INPUT) ; // вывод D2 установлен как выход Однако все выводььвнутри платы Arduino подсоединены к шине питания 5 В через резисторы сопротивлением порядка 20-50 кОм. И эти резисторы можно программно подключать к выводам или отключать от них. Программное включение резисторов осуществляется так: pinMode C, INPUT_PULLUP); // внутренний подтягивающий резистор 20 кОм подключен И схема подключения кнопки, показанная на рис. 6.3, уже оказывается верной! Теперь напишем программу (листинг 6.5), устанавливающую состояние светодио- да, подключенного к выводу 13 (находится на плате Arduino), в зависимости от состояния кнопки (кнопка нажата — светодиод горит, кнопка отпущена — светодиод потушен).
100 Часть III. Практическое применение Arduino В процедуре setup () устанавливаем режимы выводов, а в процедуре loop () — считываем состояние кнопки в переменную buttonstate и передаем его на светодиод. Для схемы с подтягивающим резистором состояние переменной buttonstate инвертируем, т. к. в этом случае при нажатой кнопке состояние сигнала низкое, а светодиод светится при высоком. Для схемы со стягивающим резистором состояние переменной buttonstate инвертировать не надо. const int LED=13; // Контакт 13 для подключения светодиода const int BUTTON=2; // Контакт 2 для подключения кнопки boolean buttonstate; // переменная статуса кнопки buttonstate void setup() { // определяем вывод LED (светодиод) как выход pinMode(LED, OUTPUT); // определяем вывод BUTTON (кнопка) как вход pinMode(BUTTON, INPUT_PULLUP); void loop () { // считываем состояние BUTTON входа (кнопки) и записываем в buttonstate buttonstate = digitalRead(BUTTON); // инверсия переменной buttonstate // для схемы с подтягивающим резистором buttonstate = ! buttonstate; // записываем состояние из buttonstate на выход LED (светодиод) digitalWrite(LED, buttonstate); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_05 сопровождающего книгу электронного архива (см. приложение 2). Загрузим этот скетч в плату Arduino — вы должны наблюдать включение светодиода на выводе 13 при нажатии кнопки и выключение его при ее отпускании. Насколько удобно держать кнопку постоянно нажатой для свечения светодиода? Гораздо удобнее иметь возможность нажать кнопку один раз, чтобы включить светодиод и, нажав ее еще раз, выключить. Загрузим в плату Arduino скетч переключения состояния светодиода при нажатии кнопки (листинг 6.6). Для отладки воспользуемся выводом данных в последовательный порт. const int LED=13; // Контакт 13 для подключения светодиода const int BUTTON=2; // Контакт 2 для подключения кнопки boolean buttonstate; // переменная статуса кнопки buttonstate
Глава 6. Проекты для изучения выводов Arduino 101 boolean buttonStatePrevKLOW; // переменная статуса кнопки предыдущая boolean ledState=LOW; // переменная статуса светодиода void setup() { // запуск последовательного порта Serial.begin(9600); // определяем вывод LED (светодиод) как выход pinMode(LED, OUTPUT); // определяем вывод BUTTON (кнопка) как вход pinMode(BUTTON, INPUT^PULLUP); // начальное состояние светодиода digitalWrite(LED, ledState); void loop () { // считываем состояние BUTTON входа (кнопки) buttonState = digitalRead(BUTTON); // если нажатие с LOW на HIGH if (buttonState == HIGH && buttonStatePrev==LOW) { ledState = ! ledState; // записываем состояние из ledState на выход LED (светодиод) digitalWrite(LED, ledState); Serial.println(ledState); } buttonStatePrev = buttonState; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\JNJN сопровождающего книгу электронного архива (см. приложение 2). Нажимаем на кнопку и видим, что в последовательном порту при однократном нажатии кнопки происходит несколько изменений ее состояния (рис. 6.6) и соответственно несколько переключений светодиода. Почему же так происходит? Дело в том, что кнопки представляют собой механические устройства с системой пружинного контакта, подверженной явлению, называемому дребезгом,— в процессе нажатия кнопки контакт, особенно в начале нажатия, несколько раз замыкается и размыкается. То есть, когда вы нажимаете на кнопку, сигнал не просто меняется от низкого до высокого — он в течение нескольких миллисекунд меняет значение от одного до другого, прежде чем установится значение low. Графики, приведенные на рис. 6.7, иллюстрируют отличие ожидаемого явления от реального. Нажатие кнопки происходит примерно за 25 мс. Вы могли бы предположить, что можете сразу узнать о состоянии кнопки, считав значение со входа контакта, как показано на левом графике. Однако кнопка фактически движется вверх-вниз, пока значение не установится, как показано на правом графике. Теперь, зная, как ведет
102 Часть III. Практическое применение Arduino При одном нажатии кнопки Рис. 6.6. Вывод данных отладки в монитор последовательного порта Идеальное нажатие кнопки Реальное нажатие кнопки Кнопка нажата Кнопка нажата Рис. 6.7. Дребезг при нажатии кнопки себя кнопка, вы можете написать программу, которая обеспечит нормальное снятие показаний с кнопки, подверженной дребезгу, — она считывает состояние кнопки и ожидает некоторое время, после чего считывает состояние снова, чтобы убедиться, что первоначальное состояние не изменилось. Эта логика программы может быть выражена следующим образом: 1. Сохраняем предыдущее состояние кнопки и текущее состояние кнопки (при инициализации low).
Глава 6. Проекты для изучения выводов Arduino 103 2. Считываем текущее состояние кнопки. 3. Если текущее состояние кнопки отличается от предыдущего ее состояния, ждем 5 мс, потому что кнопка, возможно, изменила состояние. 4. По истечении 5 мс считываем состояние кнопки и используем его в качестве текущего. 5. Если предыдущее состояние кнопки было low, а текущее high, переключаем состояние светодиода. 6. Устанавливаем предыдущее состояние кнопки для ее текущего состояния. 7. Возврат к шагу 2. Составляем скетч по приведенному алгоритму (листинг 6.7), загружаем его в плату Arduino и проверяем работоспособность. Как можно видеть, однократное нажатие кнопки приводит к однократному изменению состояния светодиода. const int LED=13; // Контакт 13 для подключения светодиода const int BUTTON=2; // Контакт 2 для подключения кнопки boolean lastButton = LOW; // Переменная для сохранения предыдущего // состояния кнопки boolean currentButton = LOW; // Переменная для сохранения текущего // состояния кнопки boolean ledOn = false; // Текущее состояние светодиода (включен/выключен) void setup () { // запуск последовательного 'порта Serial.begin(9600); pinMode (LED, OUTPUT); // Сконфигурировать контакт светодиода как выход pinMode (BUTTON, INPUT); // Сконфигурировать контакт кнопки как вход void loop() { currentButton = debounce(lastButton); if (lastButton == LOW && currentButton = HIGH) // если нажатие... { ledOn = IledOn; // инвертировать значение состояния светодиода Serial.printIn(ledOn); } lastButton = currentButton; digitalWrite(LED, ledOn); // изменить статус состояния светодиода
104 Часть III. Практическое применение Arduino // Функция сглаживания дребезга // Принимает в качестве аргумента предыдущее состояние кнопки, // выдает фактическое. boolean debounce(boolean last) { boolean current = digitalRead(BUTTON); // Считать состояние кнопки if (last != current) ' // если изменилось... { delayE); . // ждем 5 мс current = digitalRead(BUTTON); // считываем состояние кнопки return current; // возвращаем состояние кнопки Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_07 сопровождающего книгу электронного архива (см. приложение 2). 6.2.2. Управление кнопками количеством горящих светодиодов Создадим еще один проект с использованием кнопок — будем управлять с их помощью количеством горящих светодиодов. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования (монтажная плата); ? соединительные провода 1111 — 12 шт.; ? резисторы 220 Ом — 8 шт.; О светодиоды — 8 шт; ? кнопки — 2 шт.; ? резисторы 10 кОм — 2 шт. В представленной на рис. 6.8 монтажной схеме этого проекта восемь подключенных к плате Arduino светодиодов расположены в ряд. При нажатии одной кнопки добавляем горящий светодиод, при нажатии другой — убираем. Весь основной цикл программы мы постоянно опрашиваем кнопки, ожидая их нажатия. После нажатия кнопки следует реакция— изменение переменной counti (количества зажженных светодиодов). Обратите внимание, что это число не может быть меньше о и больше значения countleds. Функция changecounti о возвращает измененное состояние counti: int change_countl(int but) { if (but=0) // добавить return min(count1+1,COUNT LEDS);
Глава 6. Проекты для изучения выводов Arduino 105 else // снять return max(count1-1,0); После изменения значения counti нам необходимо установить новые состояния для светодиодов, чтобы они соответствовали новому значению counti, — нужно зажечь светодиоды с о до counti и потушить светодиоды с counti до последнего (count_leds=8). Для этого вызываем функцию setiedso, передавая ей в качестве аргумента значение counti: void setleds(int cnt) { // включение светодиодов от 0 до counti for(int i=0;i<cnt;i++) digitalWrite(pinleds[i],HIGH); // включение светодиодов от 0 до cnt for(int i=cnt;i<COUNT_LEDS;i++) digitalWrite(pinleds[i],LOW); Рис. 6.8. Монтажная схема подключения светодиодов и кнопок
106 Часть III. Практическое применение Arduino Для устранения дребезга кнопок используем функцию debounce () из листинга 6.7. Создадим в Arduino ШЕ новый скетч, занесем в него код из листинга 6.8 и загрузим скетч в плату Arduino. Напомним, что в настройках Arduino IDE необходимо выбрать тип платы (Arduino UNO) и порт подключения платы. // количество кнопок #define COUNT_BUTTONS 2 // количество светодиодов #define COUNTJLEDS 8 // Выводы Arduino для подключения // список светодиодов int pinleds[8]={4,5,6,1,8,Ъ,10,11}; // список кнопок int pinbuttons[2]={2,3}; // список сохранения предыдущих состояний кнопок int lastbuttons[2]={0,0}; // список сохранения текущих состояний кнопок int currentbuttons[2]={0,0}; // переменные - количество горящих светодиодов int count1=0; void setup() { // настроить выводы Arduino для светодиодов как выходы for(int i=0;i<COUNT_LEDS;i++) { pinMode(pinleds[i], OUTPUT); } // установить число светодиодов @) setleds(O); // the loop function runs over and over again forever void loop() { // проверка нажатия кнопок выбора программ for(int i=0;i<COUNT_BUTTONS;i++) { currentbuttons[i] = debounce(lastbuttons[i],pinbuttons[i]); // если нажатие... if (lastbuttons[i] == 0 && currentbuttons[i] = 1) { // реакция на нажатие - изменение count1 countl=change_countl(i); // обновление состояния setleds(count1);
Глава 6. Проекты для изучения выводов Arduino 107 lastbuttons[i] = currentbuttons[i]; // изменение количества светодиодов int change_countl(int but) { if (but=0) // добавить return min(count1+1,COUNT_LEDS); else // снять return max(count1-1,0); // (включение-выключение светодиодов) void setleds (irit cnt) { // включение светодиодов от 0 до count1 for(int i=0;i<cnt;i++) digitalWrite(pinleds[i],HIGH); // включение светодиодов от 0 до cnt for(int i=cnt;i<COUNT_LEDS;i++) digitalWrite(pinleds[i],LOW); } /* Функция сглаживания дребезга * Принимает в качестве аргумента предыдущее состояние кнопки * и выдает фактическое. V int debounce(int last,int pinl) { int current = digitalRead(pinl); // Считать состояние кнопки // если изменилось... if (last != current) { delayE); // ждем 5 мс current = digitalRead(pinl); // считываем состояние кнопки return current; // возвращаем состояние кнопки Электронный архив Полный вариант рассмотренного скетча находится в папке examplesW^oe^oe сопровождающего книгу электронного архива (см. приложение 2). 6.3. Аналоговые входы — светодиодный индикатор аналоговых значений В предыдущих разделах мы познакомились с цифровыми выводами Arduino, которые могут работать в режиме входов — для считывания внешних цифровых сигналов, и выходов— для выдачи цифровых данных. Но цифровые данные имеют только два состояния: high и low. В то же время мир вокруг нас является аналого-
108 Часть III. Практическое применение Arduino вым — большинство наблюдаемых явлений в окружающем нас мире чаще всего имеет аналоговый характер. Мир предполагает бесконечное число возможных состояний, будь то цвет солнечного света, или температура океана, или концентрация загрязняющих веществ в воздухе. Графики на рис. 6.9 показывают, как аналоговые и цифровые сигналы отличаются друг от друга. Слева — прямоугольная волна, значения которой варьируются только между двумя значениями: 0 и'5 В. Точно так же, как и в случае с кнопкой, которую вы использовали в предыдущем разделе, этот сигнал — только high или low. Справа — часть косинусоидальной волны. Несмотря на то что ее границы все еще О и 5 В, сигнал имеет бесконечное число значений между этими двумя напряжениями. 10 А 6 Time(s) 10 Рис. 6.9. График значений для цифровых (слева) и аналоговых (справа) сигналов Компьютерная система никогда не сможет осуществлять измерение аналогового значения с его бесконечным числом десятичных разрядов, потому что память и производительность компьютера небеспредельны. Если это так, то как вы можете соединить интерфейс цифрового Arduino с аналоговым реальным миром? Ответ прост— с помощью аналого-цифрового преобразователя (АЦП), который может преобразовать аналоговые значения в цифровые представления с конечной точностью. Каждый из аналоговых контактов Arduino (для Arduino Uno это А0-А5) содержит 10-разрядный АЦП для аналоговых преобразований. 10-разрядный здесь означает, что АЦП может разделить аналоговый сигнал на 210 различных значений B10 = 1024)— от 0 до 1023. Максимальное напряжение E В) задает опорное напряжение— его значение соответствует значению 1023 АЦП. При напряжении на контакте 0 В АЦП возвращает значение 0. Напряжение 2,5 В возвращает значение 512 (половина из 1023), и т. д.
Глава 6. Проекты для изучения выводов Arduino 109 6.3.1. Подключение потенциометра к плате Arduino Самый простой аналоговый датчик, с которого вы можете получить данные, — это потенциометр. Потенциометры являются переменными делителями напряжения и часто выглядят как ручки. Они присутствуют в стереосистемах, звуковых колонках, термостатах и в других изделиях, бывают разных размеров и форм, но все имеют три вывода. В представленной на рис. 6.10 монтажной схеме этого проекта один из крайних выводов потенциометра подключен на «землю», а другой крайний вывод — к шине +5 В. Средний вывод потенциометра подключен к аналоговому входу АО платы Arduino. Рис. 6.10. Монтажная схема подключения потенциометра к плате Arduino Для считывания данных с аналогового порта в Arduino есть функция anaiogRead (), которая возвращает аналоговое значение с аналогового входа Arduino, например: int val=analogRead(AO); Загрузим в плату Arduino скетч из листинга 6.9, обеспечивающий вывод в монитор последовательного порта значений с потенциометра, подключенного к аналоговому входу АО. // пин подключения среднего вывода потенциометра tele fine PIN_ANALOG АО // переменная для сохранения значения потенциометра int val; void setup() { // запуск последовательного порта Serial.begin(9600);
110 Часть III. Практическое применение Artiuino void loop() { // считать значение с аналогового порта val=analogRead(PIN_ANALOG); // вывести последовательный порт Serial.print("val^"); Serial.println(val); delayC00); Загрузив скетч в плату, откроем монитор последовательного порта и покрутим ручку потенциометра— значения будут меняться в промежутке от 0 до 1023 (рис. 6.11). val-398 val-398 val-398 val-398 val«39€ val-398 val-399 val«399 val-398 val-350 val-l€5 val-117 val-102 val-29 val-0 val-0 val-194 val-328 val-424 val-646 val-917 val-1023 val-1023 val-1023 val-1023 val-1023 val-1023 val-1023 val-1023 Рис. 6.11. Вывод данных с аналогового входа в монитор последовательного порта Проблемы не возникнет и в том случае, если мы захотим получить с потенциометра реальные значения сопротивления в омах, — просто составим пропорцию: 1023 — ЮкОм, val — X кОм. Тогда X = val х 10/1023.
Глава 6. Проекты для изучения выводов Arduino 111 В Arduino IDE для таких преобразований имеется встроенная функция тар (): тар (value, fromLow, fromHigh, toLow, toHigh) Функция пропорционально переносит значение value из текущего диапазона значений fromLow . . . fromHigh В НОВЫЙ диапазон toLow .. . toHigh. Обратите внимание, что «нижняя граница» может быть как меньше, так и больше «верхней границы». Этим можно воспользоваться, чтобы «перевернуть» диапазон: у = map(x, 1, 50, 50, 1); Возможно использование и отрицательных значений: у = шар(х, 1, 50, 50, -100); Функция тар () оперирует целыми числами. При пропорциональном переносе дробная часть не округляется по правилам, а просто отбрасывается. Внесем изменения в предыдущий скетч (см. листинг 6.9), чтобы в монитор выводились не только показания аналогового входа АО, но и чтобы мы могли видеть выставляемые значения потенциометра в омах, и получим скетч, представленный в листинге 6.10. Загружаем в плату Arduino этот скетч и смотрим вывод показаний потенциометра в омах (рис. 6.12). // пин подключения среднего вывода потенциометра #define PIN_ANALOG АО // переменная для сохранения значения потенциометра int val; unsigned int valQm; void setup() { // запуск последовательного порта Serial.begin(9600); } void loop() { // считать значение с аналогового порта val=analogRead(PIN_ANALOG); // переводим в Ом valQm=map(val,0,1023,0,10000); // вывести последовательный порт Serial.print("val="); Serial.print(valOm) ; Serial.println(" Qm"); delayC00);
112 Часть III. Практическое применение Arduino val-10000 On val-10000 Ob val*10QOO Om val«9266 On val~7849 On val-7028 On val-6402 On val-5503 On val-4017 On val-2482 On val-1104 On val-0 On val-Q On val-0 On val«0 On val-0 On val«0 On val-1221 On va1-2932 On val-4467 On val-5522 On val-6304 On val-6285 On val-6471 On val-6510 On val-6510 On val-6510 On val-6510 On Om Рис. 6.12. Вывод показаний потенциометра в монитор последовательного порта в омах 6.3.2. Вывод показаний потенциометра на светодиодную шкалу Теперь визуализируем аналоговые данные потенциометра с помощью светодиодной шкалы, состоящей из 10 светодиодов. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования; ? 10-разрядная линейная светодиодная шкала; ? резистор 220 Ом — 10 шт.; ? соединительные провода ПП — 15 шт. В представленной на рис. 6.13 монтажной схеме этого проекта для подключения светодиодной шкалы к Arduino мы задействуем 10 цифровых выводов: D3-D12. Каждый из светодиодов шкалы выводом анода соединен с цифровым выводом Arduino, а катодом— с «землей» через последовательно включенный ограничи-
Глава 6. Проекты для изучения выводов Arduino 113 вающий резистор 220 Ом. Аналоговые данные потенциометра @-1023) масштабируем в данные шкалы @-10) с помощью функции тар () и зажигаем на ней соответствующее количество светодиодов (листинг 6.11). Рис. 6.13. Монтажная схема подключения к Arduino индикатора показаний потенциометра на светодиодах // Аналоговый вход АО для подключения потенциометра const int POT=0; // переменная для хранения значения потенциометра int valpot = 0; // список контактов подключения светодиодов const int pinsled[10]={4,5,6,7,8,9,10,11,12,13}; // переменная для хранения значения шкалы int countleds = 0; void setup() { // запуск последовательного порта Serial.begin(9600); for(int i=0;i<10;i++) { // Сконфигурировать контакты подсоединения шкалы как выходы pinMode(pinsled[i],OUTPUT); digitalWrite(pinsled[i],LOW);
114 Часть III. Практическое применение Arduino void loop () { // чтение данных потенциометра valpot = analogRead(POT); // масштабируем значение к интервалу 0-10 countled=map(valpot,0,1023,0,10); Serial.print("countled ="); Serial.println(countled); // зажигаем количество светодиодов на шкале, равное countled .for(int i=0;i<10;i++) { if(i<countleds) // зажигаем светодиод шкалы digitalWrite(pinsled[i]fHIGH); else // гасим светодиод шкалы digitalWrite(pinsled[i],LOW); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_11 сопровождающего книгу электронного архива (см. приложение 2). Загружаем скетч в плату Arduino и, поворачивая ручку потенциометра, следим за показаниями шкалы из светодиодов. Данные для отладки выводим в монитор последовательного порта. 6.4. ШИМ — радуга на RGB-светодиоде Arduino не может на цифровой выход выдавать произвольное напряжение: выдается либо +5 В (high), либо 0 В (low). Но уровнем напряжения управляется многое, например: яркость светодиода, скорость вращения мотора или звук пьезоизлучате- ля. Решение этой проблемы обеспечивает широтно-импулъсная модуляция (ШИМ) — процедура получения изменяющегося аналогового значения с использованием цифровых сигналов. Работу с помощью ШИМ на Arduino обеспечивают «особые» цифровые выводы, называемые PWM (Pulse-width modulation, широтно-импульсная модуляция) и обозначенные на плате волнистой линией примерно такого вида ~. На платах Arduino Nano и Arduino Uno ШИМ поддерживают выводы 3, 5, 6, 9-11, на плате Mega — выводы 2-13. Принцип организации ШИМ основан на том, что цифровой сигнал на выходе постоянно переключается между максимальным и минимальным значениями. Длительность включения максимального значения называется шириной импульса. При этом отношение ширины импульса к периоду его следования (скваэюность) можно изменять (рис. 6Л 4). Если скважность равняется 100%, то все время на цифровом выходе Arduino будет напряжение логической единицы E В). Если задать скважность 50%, то половину времени на выходе будет логическая единица, а половину— логический ноль и среднее напряжение будет равняться 2,5 В. И так далее.
Глава 6. Проекты для изучения выводов Arduino 115 Таким образом, с помощью задания скважности можно менять среднее напряжение на выходе ШИМ. Выводы Arduino с функцией широтно-импульсной модуляции работают на частоте около 500 Гц. Глаз не замечает мерцания более 50 Гц, поэтому нам кажется, что светодиод, на который подается ШИМ-сигнал, не мерцает, а лишь горит в неполную силу — пропорционально скважности этого сигнала. Широтно-Импульсиая модуляция 0% рабочего цикла - analogWrite(O) 5v 0v 5v Ov 5v Ov Sv Ov 5v Ov 2$% рабочего цикла - anatogWrtteF4) fUUUUU 50% рабочего цикла - anaiogWHte(i27) ППЛ-П-Ш 75% рабочего цикла - anatogWrtteA91) ппппгц 100% рабочего цикла - analogWrtt0B55) Красный . Общий 1234 Рис. 6.14. Принцип работы широтно-импульсной модуляции (ШИМ) Рис. 6.15. Контакты RGB-светодиода Посмотрим, как на основе ШИМ управлять яркостью светодиода. Создадим для этого проект генерации любого цвета, воспользовавшись специальным светодио- дом RGB. Аббревиатура RGB расшифровывается как Red, Green, Blue (красный, зеленый, синий)— из этих цветов можно получить любой цвет путем смешения исходных. Светодиод RGB отличается от обычного тем, что содержит три небольших кристалла: R, G и В, с помощью которых можно синтезировать любой цвет или оттенок, в зависимости от подаваемых на них напряжений. RGB-светодиод имеет 4 вывода (рис. 6.15). 6.4.1. Подключение к плате Arduino RGB-светодиода Подключим RGB-светодиод к плате Arduino и заставим его переливаться всеми цветами радуги. В представленной на рис. 6.16 монтажной схеме этого проекта присутствует и потенциометр, с помощью которого мы сможем регулировать скорость изменения этих цветов.
116 Часть III. Практическое применение Arduino аг^^ Рис. 6.16. Монтажная схема подключения RGB-светодиода для свечения всеми цветами радуги Список семи основных цветов радуги с разложением их по компонентам R, G и В представлен в табл. 6.1. Таблица 6.1. Семь основных цветов радуги с разложением по компонентам R, G и В Цвет Красный Оранжевый Желтый Зеленый Голубой Синий Фиолетовый R 255 255 255 0 0 0 255 G 0 125 255 255 255 0 0 В 0 0 0 0 255 255 255 Наш светодиод должен переливаться от красного цвета до фиолетового, проходя через все семь основных цветов. Алгоритм вычисления любого промежуточного цвета радуги следующий: 1. Примем за начальную точку отсчета красный цвет B55, 0, 0). 2. Будем постепенно увеличивать значение зеленой составляющей G, пока не достигнем значения оранжевого B55, 125, 0), а затем и желтого цвета B55,255, 0). 3. Постепенно уменьшим значение красной составляющей R до значения зеленого цвета @, 255, 0).
Глава 6. Проекты для изучения выводов Arduino 117 4. Постепенно увеличим значение синей составляющей В до значения голубого цвета @, 255, 255). 5. Постепенно уменьшим количество зеленой составляющей G до значения синего цвета @, 0,255). 6. Постепенно увеличим количество красной составляющей R до значения фиолетового цвета B55, 0,255). 7. Выдерживаем небольшую паузу и переходим к шагу 1. Содержимое скетча, реализующего этот алгоритм, приведено в листинге 6.12. // пауза перед каждым изменением цвета радуги tdefine MAX_PAUSE 30 #define MINJPAUSE 1 // пин подключения среднего вывода потенциометра const int PIN_POT=A0; // вывод красной ноги RGB-светодиода const int RED=11; // вывод зеленой ноги RGB-светодиода const int GREEN=10; // вывод синей ноги RGB-светодиода. const int BLUE=9; // переменная для хранения значения потенциометра int pot; // переменная для хранения R-составляющей цвета int red; // переменная для хранения G-составляющей цвета int green; // переменная для хранения В-составляющей цвета int blue; void setup() void loop () { //от красного к желтому red=2 55;green=0;blue=0; for(green=0;green<=255;green++) setRGB(red,green,blue); //от желтого к зеленому for(red=255;red>=0;red—) setRGB(red,green,blue); //от зеленого к голубому for(blue=0;blue<=255;blue++) setRGB(red,green,blue);
118 Часть III. Практическое применение Arduino //от голубого к синему for(green=255;green>=0;green—) setRGB(red,green,blue); //от синего к фиолетовому for(red=0;red<=255;red++) setRGB(red,green,blue); delayB000); } // функция установки цвета RGB-светодиода void setRGB(int r,int g,int b) { analogWrite(RED,r); analogWrite(GREEN,g); analogWrite(BLUE,b); pot=analogRead(PIN_POT); delay(шар(pot,0,1023, MIN_PAUSE, MAX_PAUSE) Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_12 сопровождающего книгу электронного архива (см. приложение 2). Загружаем скетч в плату и наблюдаем свечение RGB-светодиода всеми цветами радуги. Скорость изменения цветов регулируем с помощью потенциометра. 6.5. Светодиодные индикаторы Светодиодный индикатор представляет собой группу светодиодов, расположенных в определенном порядке и объединенных конструктивно. Рассмотрим один из самых распространенных светодиодных индикаторов— светодиодный семисег- ментный индикатор. В светодиодных семисегментных индикаторах контакты промаркированы метками от а до g (и дополнительно DP — для отображения десятичной точки). У них также имеется один общий вывод, который определяет тип подключения индикатора: схема с общим анодом (ОА) или с общим катодом (ОК). Зажигая одновременно несколько светодиодов, можно формировать на индикаторе символы цифр. Назначение контактов светодиодного семисегментного индикатора D5651 с общим катодом показано на рис. 6.17. 6.5.1. Подключение к плате Arduino семисегментного индикатора Создадим проект вывода на семисегментный индикатор цифр от 0 до 9. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; D кабель USB;
Глава 6. Проекты для изучения выводов Arduino 119 Ю 9,8 12 3 4 5 Рис. 6.17. Назначение контактов индикатора D5651 с общим катодом ? плата прототипирования; ? семисегментный индикатор D5651; ? резистор 220 Ом — 8 шт.; ? соединительные провода ПП— 15 шт. Монтажная схема подключения семисегментного индикатора D5651 к плате Arduino показана на рис. 6.18. При создании скетча для хранения информации о каждой цифре будем использовать 1 байт, который состоит из 8 битов (каждый бит: 0 или 1). Данные для представления каждой цифры приведены в табл. 6.2. Рис. 6.18. Монтажная схема подключения семисегментного индикатора D5651 к плате Arduino
120 Часть III. Практическое применение Arduino Цифра 0 1 2 3 4 5 6 7 8 9 а бит 7 1 0 1 1 0 1 .1 1 1 1 b бит б 1 1 1 1 1 0 0 1 1 1 Таблица Сегменты с бит 5 1 1 0 1 1 1 1 1 1 1 d бит 4 1 0 1 1 0 1 1 0 1 1 6.2. Значения битов для каждой цифры индикатора е битЗ 1 0 1 0 0 0 1 0 1 0 f бит 2 1 0 0 0 1 1 1 0 1 1 g бит1 0 0 1 1 1 1 1 0 1 1 DP битО 0 0 0 0 0 0 0 0 0 0 Число в двоичной форме В11111100 В01100000 В11011010 В11110010 В01100110 В10110110 В10111110 В11100000 В11111110 В11110110 Организуем массив для описания 10 цифр (от 0 до 9): // значения для вывода цифр 0-9 byte numbers[10] = { В11111100, // 0 В01100000, // 1 B11011010, // 2 B11110010, // 3 B01100110, // 4 B10110110, // 5 B10111110, // 6 B11100000, // 7 B11111110, // 8 B11100110 // 9 Теперь напишем подпрограмму setNumber (), отвечающую за вывод значений на контакты Arduino для индикации цифры. Воспользуемся при этом Arduino-функ- цией bitReadO, которая возвращает состояние указанного бита числа A илиО). Нумерация начинается с младшего значащего бита (крайнего правого) с номером 0: x=bitRead(B01001000,б); // х=1 - шестой бит, начиная с нулевого (крайнего справа) А вот и сама подпрограмма: void setNumber (int num) { // пройти по всем битам for(int i=0;i<7;i++) { if(bitRead(numbers[num],7-i)==HIGH) // зажечь сегмент digitalWrite(pins[i],HIGH);
Глава 6. Проекты для изучения выводов Arduino 121 else // потушить сегмент digitalWrite(pins[i],LOW); Каждую секунду (реализуем это с помощью delay (ЮОО)) мы будем выводить на индикатор новое число от 0 до 9 (листинг 6.13). // список выводов Arduino для подключения к разрядам а-д // семисегментного индикатора int pins[7]={11,10,9,8,7,6,5,4}; // значения для вывода цифр 0-9 byte numbers[10] = { В11111100, // 0 В01100000, // 1 B11011010, // 2 B11110010, // 3 B01100110, // 4 B10110110, // 5 B10111110, // 6 B11100000, // 7 B11111110, // 8 B11100110 // 9 }; // переменная для хранения значения текущей цифры int number=0; void setup() { // Сконфигурировать контакты как выходы for(int i=0;i<7;i++) pinMode(pins[i],OUTPUT); void loop () { setNumber(number); delayA000); // следующая цифра number=(number+l)%10; // функция вывода цифры на семисегментный индикатор void setNumber(int num) { for(int i=0;i<7;i++) { if(bitRead(numbers[num],7-i)==HIGH) // зажечь сегмент digitalWrite(pins[i],HIGH);
122 Часть III. Практическое применение Arduino else // потушить сегмент digitalWrite(pins[i]fLOW); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_i3 сопровождающего книгу электронного архива (см. приложение 2). Загружаем скетч в плату Arduino и смотрим на изменение цифр, выводимых на светодиодный индикатор. 6.6. Расширение цифровых выходов — микросхема 74НС595 Плата Arduino Uno имеет 20 выводов, которые можно задействовать в разработке, но бывают проекты, для которых существующих выводов недостаточно. Например, в очень простом проекте из предыдущего раздела мы использовали уже 13 выводов, а более сложные проекты потребуют еще большего их количества. Конечно можно обратиться к плате Arduino Mega, но это не всегда рационально, к тому же могут быть задуманы проекты, для реализации которых не хватит и выводов Arduino Mega. Что же делать в такой ситуации? Использовать внешние микросхемы, имеющие функционал по расширению выводов, — например, сдвиговый регистр 74НС595 (рис. 6.19). Это восьмиразрядный сдвиговый регистр с последовательным вводом, последовательным или параллельным выводом информации, с триггером-защелкой и тремя состояниями на выходе. Распиновка контактов сдвигового регистра 74НС595 показана на рис. 6.20, а назначение этих контактов представлено в табл. 6.3. 74НС595 Q1 Q2 Q3 Q4 Q5 Q6 Q7 GND VCC Q0 DS ОЕ ST_CP SH_CP MR Q7' 16 ¦MM 15 14 13 мша 12 — 10 Рис. 6.19. Сдвиговый регистр 74НС595 Рис. 6.20. Распиновка контактов сдвигового регистра 74НС595
Глава 6. Проекты для изучения выводов Arduino 123 Таблица 6.3. Назначение контактов микросхемы 74НС595 Контакт Vcc Q0...Q7 . DS ОЕ ST.CP SH_CP MR Q71 GND Назначение Питание B-6 В) Параллельные выходы Вход для последовательных данных Вход для переключения состояния выходов из высокоомного в рабочее (активация при получении LOW) Синхронизация («защелкивание») выходов Вход для тактовых импульсов Сброс значений регистра (активация при получении low) Выход для последовательного соединения регистров «Земля» Данные к микросхеме передаются последовательно по входу DS. Биты следуют в регистр друг за другом, считывание битов происходит при поступлении синхроимпульса SHCP. После поступления всех битов во внутренние ячейки 74НС595 необходимо передать их на выводы Q0...Q7 («защелкнуть»). «Защелкивание» происходит при поступлении синхроимпульса ST_CP. Используя всего три выхода Arduino, можно управлять 8 выходами сдвигового регистра. Передал 8 битов — получил 8 выходных состояний на выходах регистра. Поступающие на DS данные при переполнении 8 внутренних ячеек проталкиваются на выход Q7'. А если микросхемы соединить последовательно друг за другом (Q7' — к выводу DS следующего сдвигового регистра 74НС595 и т. д.), то количество контролируемых выходов можно наращивать до любого разумного предела, поскольку при каскадном включении 74НС595 (при необходимости получения 16, 24 и т. д. выходов) данные от первого регистра передаются к следующему. 6.6.1. Подключение к плате Arduino сдвигового регистра 74НС595 Рассмотрим подключение к плате Arduino сдвигового регистра 74НС595 и реализацию «бегущего огня» на 8 светодиодах. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования; ? микросхема 74НС5951; ? светодиоды — 8 шт.;
124 Часть III. Практическое применение Arduino ? резисторы 220 Ом — 8 шт.; ? соединительные провода ПП — 25. Монтажная схема подключения 8 светодиодов к плате Arduino с использованием сдвигового регистра 74НС595 показана на рис. 6.21. Рис. 6.21. Монтажная схема подключения 8 светодиодов к плате Arduino с использованием сдвигового регистра 74НС595 На Arduino для подключения к сдвиговому регистру 74НС595 мы задействуем три следующих выхода (табл. 6.4). Таблица 6.4. Подключение Arduino к сдвиговому регистру 74НС595 Контакты Arduino Uno D7 D6 D5 Контакты 74НС595 DS ST_CP SH_CP Данные, которые нам необходимо отправлять в сдвиговый регистр (нулевой бит мы отправляем первым, и он попадает на выход Q7 сдвигового регистра), приведены в табл. 6.5.
Глава 6. Проекты для изучения выводов Arduino 125 Таблица 6,5. Данные, отправляемые в сдвиговый регистр Номер зажигаемого светодиода 0 1 2 3 4 5 6 7 Двоичные данные В00000001 воооооою воооооюо вооооюоо воооюооо В00100000 В01000000 В1000000 Создадим в скетче массив с данными, которые будем отправлять в сдвиговый регистр: // массив хранения значений шкалы 0-7- byte numbers[8] = { В00000001, // 0 В000000Ю, // 1 ВОООООЮО, // 2 B00001000, // 3 ВОООЮООО, // 4 B00100000, // 5- B01000000, // 6 B10000000 // 7 }; И переменную, указывающую на текущие данные: int pos=0; А также направление изменения pos — переменную dir A или -1): int dir=l; Каждую секунду будем изменять значение pos (от 0 до 8 и обратно), выбирать новые значения из массива numbers и отправлять в сдвиговый регистр: delayA000); pos=pos+dir; send_data_74hc595(number[pos]); if(pos==8) dir=-l; if (pos=0) dir=l; Теперь напишем процедуру отправки последовательных данных в сдвиговый регистр — send_data_74hc595 о. Прежде всего выставляем на D5 (вывод SH_CP сдвигового регистра) низкий уровень low. На D7 выставляем значение бита, начиная с
126 Часть III. Практическое применение Arduino младшего, затем выставляем на D5 высокий уровень high. Для получения значения бита используем Arduino-функцию bitRead(x,n), где п— номер бита @-7) в байте х, начиная с младшего: digitalWriteE, LOW) ; digitalWriteG, bitRead(x,0)); digitalWriteE, HIGH); Значение выставленного бита записывается во внутреннюю ячейку сдвигового регистра. Повторяем операцию для следующих битов: for(int i=0;i<8;i++) { digitalWriteE/ LOW); digitalWriteG, bitRead(x,i)); digitalWriteE, HIGH); } После отправки 8 битов необходимо значения «защелкнуть» на выходах Q0...Q7: digitalWriteF, HIGH); digitalWriteF, LOW); Таким образом, весь код процедуры send_data_74hc595 () выглядит так: void send_data_74hc595 (byte value) { for(int i=0;i<8;i++) {. digitalWriteE, LOW); digitalWriteG, bitRead(value,i)); digitalWi;ite E, HIGH) ; } digitalWriteF, HIGH); digitalWriteF, LOW); } Полный код скетча приведен в листинге 6.14. Загружаем скетч в плату Arduino и проверяем его работу. // константы подключения пинов const int data_pin =7; const int shjpin =5; const int st_pin = 6; // массив хранения значений шкалы 0-7 byte numbers[8] = { B00000001, B00000010, B00000100, B000010O0, B00010000, BOO1OOOOO, // 0 // 1 // 2 // 3 // 4 // 5
Глава 6. Проекты для изучения выводов Arduino 127 В01000000, // б В10000000 // 7 }; // текущее значение 0-7 int pos = 0; // текущее направление движения значения 1 или -1 int dir = 1; void setup () { // запуск последовательного порта Serial.begin(9600); // Сконфигурировать контакты подсоединения 74НС595 pinMode(data_pin,OUTPUT); pinMode(sh_pin,OUTPUT); pinMode(stjpin,OUTPUT); digitalWrite(st_pin, LOW); void loopO { // изменить текущее значение pos=pos+dir; // отправить данные в сдвиговый регистр send_data_74hc595(numbers[pos]); // для отладки Serial.print("pos=");Serial.print(pos); Serial.print(" dir=")/Serial.print(dir); Serial.print(" value=");Serial.println(numbers[pos]); // при крайних позициях поменять направление if(pos=7) dir=-l; if(pos==0) dir=l; // пауза 1 с delayA000); } // процедура отправки данных //в сдвиговый регистр void send_data_74hc595(byte value) { // последовательная отправка байта for(int i=0;i<8;i++) { digitalWrite(sh_pin, LOW); digitalWrite(data_pinf bitRead(value,i)); digitalWrite(sh_pin, HIGH); } // защелкиваем digitalWrite(st_pin, HIGH); digitalWrite(st_pinf LOW);
128 Часть III. Практическое применение Arduino Электронный архив Полный вариант рассмотренного скетча находится в папке ждающего книгу электронного архива (см. приложение 2). ^U сопрово6.7. Расширение цифровых входов и выходов — микросхема МСР23017 В предыдущем разделе мы познакомились с расширителем выводов — сдвиговым регистром 74НС595. Есть и другие способы расширить количество цифровых контактов платы Arduino. Так, микросхема МСР23017 (рис. 6.22) добавляет Arduino 16 портов, которые можно настроить как на вход, так и на выход. Микросхема использует популярную двухпроводную шину 12С. GPBO GPB1 GPB2 GPB3 GPB4 GPB5 GPB6 GPB7 Vdd Vss NC SCL SDA NC • 1 2 3 4 5 6 7 8 9 10 11 12 13 14 К I ? SE 28 27 26 25 24 23 22 21 20 19 18 17 16 15 " л щ «1 "*• ** "Г -^ ¦ ¦ jfcr ¦П ^g.r,. m 2—** 2—^ 2*— 2**— GPA7 GPA6 GPA5 GPA4 GPA3 GPA2 GPA1 GPA0 INTA 1NTB RESET А2 А1 АО Рис. 6.22. Выводы микросхемы МСР23017 Адрес микросхемы МСР23017 для протокола 12С можно установить комбинацией сигналов на цифровых входах А0-А2 (рис. 6.23), что позволяет подключить к плате Arduino одновременно 8 микросхем МСР23017 — соответственно 16x8= 128 контактов. Рис. 6.23. Установка адреса микросхемы МСР23017
Глава 6. Проекты для изучения выводов Arduino 129^ Микросхема МСР23017 имеет два банка портов: A (GPA0-GPA7) и В (GPB0-GPAB), каждый из которых можно настроить на ввод или на вывод. В листинге 6.15 показан пример настройки банков выводов А и В. // подключение библиотеки Wire.h #include <Wire.h> byte input=0; void setup() { Serial.begin(9600) ; Wire.begin@,2); // запуск 12С Wire.beginTransmission@x20); // i2c - адрес (А0-0,А1-0,А2-0) Wire.write@x00); // IODIRA register Wire.write@x00); // настроить PORT А как output Wire. endTransmission (); void loop() { // чтение данных из PORT В Wire.beginTransmission@x20); Wire.write @x13) ; Wire. endTransmission (); Wire, request From @x20, 1) ; input=Wire. read (); // записать полученные данные в PORT A Wire.beginTransmission@x20) ; Wire.write@x12); // address PORT A Wire.write(input); // PORT A Wire.endTransmission (); delayA00); // пауза Электронный архив Полный вариант рассмотренного скетча находится в папке examples\6\_06_15 сопровождающего книгу электронного архива (см. приложение 2). 6.8. Расширение аналоговых входов — мультиплексор CD4051 Если вашим проектам окажется недостаточно имеющихся в наличии на платах Arduino аналоговых входов, можно воспользоваться мультиплексором CD4051 (рис. 6.24).
130 Часть III. Практическое применение Arduino т ilillL • 1 т л т Рис. 6.24. Мультиплексор CD4051 Микросхема CD4051 представляет собой 8-канальный аналоговый мультиплексор/ демультиплексор, имеющий 8 входов (уО-у7) и один выход z (рис. 6.25). Выбор считываемого входа осуществляется подачей цифровых сигналов на выходы sO-s2. Таким образом, для подключения к плате Arduino 8 дополнительных аналоговых датчиков необходимо задействовать три цифровых выхода модуля и один аналоговый вход. 4051 в режиме мультиплексора у4 — уб- Z — У7- у5- Е- Vee- gnd- -1 -2 -3 -4 -5 -6 -7 -8 4051 16- 15- 14- 13- 12- 11- 10- 9- Vcc У2 . у1 уо уз sO s1 s2 Аналоговые входы Выбор аналогового входа (установка с 3 контактов Arduino) с выбранного аналогового ' входа (к аналоговому входу Arduino) Назначение контактов Логическая схема работы Рис. 6.25. Контакты мультиплексора CD4051 В листинге 6.16 представлен скетч циклического опроса 8 аналоговых датчиков, подключенных к 8 входам мультиплексора и — через вход z — к аналоговому входу АО платы Arduino. // список пинов для подключения к sO, si, s2 мультиплексора // D5, D7, D8 int pins[J={14, 13, 15}; // Массив двоичных чисел, определяющих номер выбранного входа/выхода // микросхемы 4051, с 1 по 8. int bin [] = { В000, В001, ВОЮ, ВОН, В100, В101, В110, Bill } ;
Глава 6. Проекты для изучения выводов Arduino 131 // служебные переменные int row; int rO = 0; int rl = 0; int r2 = 0; int avalue =0; void setup (void) { // входы подключения к мультиплексору как OUTPUT for(int i=0;i<3;i++) { pinMode(pins[i],OUTPUT); } // запуск последовательного порта Serial.begin(9600); void loop (void) { for(int i=Q;i<8;i++) { // выбор входа мультиплексора row = bin [i] ; rO = row & 0x01 ; rl = (row » 1) & 0x01 ; r2 = (row » 2) & 0x01 ; digitalWrite (pins[i]/ rO) ; digitalWrite (pins[i], rl) ; digitalWrite (pins [ i], r2) ; // получение данных с АО avalue= analogRead(AO); // вывод в монитор последовательного порта Serial.print("analog input =");Serial.print(i); Serial.println(" = "); Serial.println(avalue); } // пауза delayB000); Электронный архив Полный вариант рассмотренного скетча находится в папке вхат^ввХб^Об^б сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 7 Использование библиотек в проектах Arduino Возможности среды программирования Arduino могут быть существенно расширены за счет использования библиотек. Библиотеки расширяют функциональность программ и несут в себе дополнительные функции — например, для работы с аппаратными средствами, по обработке данных и т. д. Ряд библиотек устанавливается автоматически вместе со средой разработки, однако вы также можете скачивать дополнительные библиотеки или создавать собственные. Использование библиотек существенно упрощает работу над проектами, потому что дает возможность сосредоточиться на основной логике программы, не тратя время на множество мелочей, Сегодня огромное количество библиотек выложено в Интернете, где их можно легко скачать, причем совершенно бесплатно. Электронный архив Необходимые для работы с проектами книги подгружаемые библиотеки размещены в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). 7.1. Установка библиотек Среда разработки Arduino IDE поставляется вместе с набором стандартных библиотек. Функционал стандартных библиотек включает в себя функции базовых видов коммуникации и поддерживает наиболее распространенные устройства. Стандартные библиотеки располагаются в папке libraries каталога установки Arduino. Список всех установленных библиотек, в том числе и базовых, можно посмотреть в Arduino IDE по команде Эскиз | Include Library (рис. 7.1). Если в этом списке нет необходимой библиотеки, ее нужно установить. Сделать это можно тремя способами: ? через Менеджер библиотек (Manage libraries); ? импортированием ZDP-архива библиотеки; ? вручную. Установленная библиотека появится в списке библиотек Arduino IDE, и ее можно будет использовать в своих скетчах. Обратите внимание: примеры добавлен-
Глава 7. Использование библиотек в проектах Arduino 133 Рис. 7.1. Список установленных библиотек в Arduino IDE ной библиотеки появятся в меню Файл | Образцы только после перезагрузки Arduino IDE. 7.1.1. Установка библиотеки через Менеджер библиотек Эта возможность появилась в Arduino IDE, начиная с версии 1.6.2,— соответствующее меню открывается командой Эскиз | Include Library | Manage Libraries. В появившемся списке (рис. 7.2) ищем нужную библиотеку, выбираем необходимую версию и нажимаем кнопку Install. Время загрузки зависит, как правило, от скорости интернет-соединения. По завершении загрузки рядом с названием библиотеки должна появиться надпись INSTALLED (рис. 7.3). После этого Менеджер библиотек можно закрыть. 7.1.2. Установка библиотеки из ZIP-архива Многие библиотеки в Интернете предлагаются для установки из ZIP-архивов. Для подключения таких библиотек необходимо их сначала скачать на компьютер. Затем в Arduino IDE выполнить команду меню Эскиз | Include Library | Add /ZIP library, в открывшемся окне выбрать путь к скачанному ZIP-архиву и нажать кнопку Open (рис. 7.4) — ZIP-архив библиотеки будет распакован в папку libraries
134 Часть III. Практическое применение Arduino Рис. 7.2. Поиск библиотеки в Менеджере библиотек Рис. 7.3. Библиотека установлена
Глава 7. Использование библиотек в проектах Arduino 135 Рис. 7.4. Установка библиотеки из ZIP-архива каталога установки Arduino, а название библиотеки появится в списке установленных библиотек. 7.1.3. Установка библиотеки вручную Для ручной установки библиотеки необходимо скачать и распаковать ее ZIP-архив. Название распакованной папки является названием библиотеки. Внутри этой папки находятся: файл с расширением срр, файл с расширением h, текстовый файл keywords.txt, папка examples с примерами и другие файлы, требуемые библиотеке. Распакованную папку необходимо переместить в папку libraries, расположение которой зависит от используемой вами операционной системы: ? для Windows по умолчанию это Мои floicyMeHTbiWduinoMibraries; О для macOS: -/Документы/Arduino/libraries; ? в Linux это будет папка libraries с вашими скетчами. После перезапуска Arduino IDE добавленная библиотека станет доступна для подключения ее к своим программам через меню Эскиз | Include Library. 7.2. Подключение библиотеки Для того чтобы подключить библиотеку, нужно написать всего одну строку в начале скетча: #include <name_library.h>
136 Часть III. Практическое применение Arduino например: #include < LiquidCrystal.h> Некоторые библиотеки при работе используют методы и функции других библиотек — тогда нужно подключать обе библиотеки: сначала подключается та, методы и функции которой использует вторая, например: // Подключение библиотеки Wire для работы с шиной I2C #include <Wire.h> // Подключение библиотеки LiquidCrystal_I2C #include <LiquidCrystal_I2C.h> Для работы с большинством библиотек нужно создать объект (экземпляр класса библиотеки), через который будут доступны их функции и методы, например: LiquidCrystal_I2C led@x27,20,4); Здесь led— это объект библиотеки LiquidCrystai_i2c, через него и обращаются к функциям и методам библиотеки. 7.3. Создание собственной библиотеки Научимся создавать собственные библиотеки. В разд. 6.5 мы рассматривали работу со светодиодным индикатором D5651. Создадим библиотеку, которая будет упрощать вывод цифр на этот индикатор. Назовем ее D5651. Библиотека должна иметь как минимум два файла: ? заголовочный файл (с расширением h); ? файл с исходным кодом (с расширением срр). В первом файле содержится описание самого класса, переменные и константы. А второй файл содержит программный код методов. 7.3.1. Создание заголовочного файла D5651.h Все содержимое h-файла необходимо заключить в конструкцию, исключающую повторное подключение библиотеки: #ifndef Button_h // если библиотека Button не подключена #define Button_h // тогда подключаем ее #endif Внутри конструкции необходимо включить файл Arduino.h, содержащий стандартные константы и переменные языка Arduino. В обычных программах он добавляется автоматически, а для библиотеки должен быть указан явно, поэтому следует написать: #include "Arduino.h"
Глава 7. Использование библиотек в проектах Arduino 137^ Необходимо также добавить описание класса. Класс — это набор функций и переменных, объединенных в одном месте. Функции и переменные могут быть публичными (public), что означает общий доступ к ним всех, кто использует библиотеку, или частными (private), что означает доступ к ним только внутри класса. Конструктор имеет то же имя, что и класс, но не имеет типа возвращаемого значения: class D5651 { public: D5651(byte *pins); void setNumber«(int num) ; private: byte *pinsS; }; Содержимое создаваемого файла D5651.h приведено в листинге 7.1. iifndef D5651_h idefine D5651_h iinclude "Arduino.h" class D5651 { public: D5651(byte *pins); void setNumber(int num); private: byte *pinsS; }; tendif 7.3.2. Создание файла реализации D5651.cpp В начале кода этого файла находится директива #inciude: tinclude <D5651.h> Она разрешает доступ к характеристикам, содержащимся в головном файле библиотеки D5651.h. За ней следует конструктор. Он используется для создания экземпляра создаваемого класса: D5651::D5651(byte *pins) { for(int i=0;i<8;i++) { pinsS= pins;
138 Часть III. Практическое применение Arduina for(int i=0;i< pinMode(pinsS[i], OUTPUT); // определяем вывод как вход Далее прописана реализация метода. Код D5651:: означает, что функция принадле жит классу D5651. Содержимое файла D5651.cpp приведено в листинге 7.2. #include <D5651.h> // описание конструктора класса D5651 D5651::D5651(byte *pins) { for(int i=0;i<8;i pinsS= pins; for(int i=0;i pinMode(pinsS[i], OUTPUT); // определяем вывод как вход // вывод цифры void D5651::setNumber(int num) { byte numbers[10] = { B11111100, // 0 В01100000, B11O11O1O, B1111OO1O, BO11OO11O, B1O11O11O, B1O11111O, B111OOOOO, B1111111O, B111OO11O // // // // // // // // // 1 2 3 4 5 6 7 8 9 for(int i=0;i<7;i++) { if(bitRead(numbers[num],7-i)=fHIGH) // зажечь сегмент digitalWrite(pinsS[i],HIGH); else // потушить сегмент digitalWrite(pinsS[i],LOW); 7.3.3. Создание файла keywords.txt Для того чтобы Arduino ШЕ выделяла цветом новые типы и методы нашей библиотеки, необходимо создать файл keywords.txt:
Глава 7. Использование библиотек в проектах Arduino 139 D5651 KEYW0RD1 setNumber KEYW0RD2 Каждая строка этого файла содержит ключевое слово, табуляцию (не пробелы) и тип ключевого слова: keywordi определяет классы, keyword2 — методы. Электронный архив ZIP-архив с файлами библиотеки D5651 находится в папке examp/es\7 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 8 00 Arduino и последовательный порт UART Протокол обмена через последовательный порт UART предполагает двусторонний обмен данными между двумя устройствами. При этом оба устройства являются равноправными — то есть явно выраженного деления устройств на главное и подчиненное не предполагается. Для организации обмена данными используются всего две линии: по одной данные передаются от первого устройства ко второму, а по другой — в обратном направлении. На микроконтроллере при этом задействуются два контакта (пина). Обозначаются они соответственно: ТХ — передающий и RX — принимающий. Линии передачи соединяют ТХ первого устройства с RX второго и RX первого устройства с ТХ второго. Arduino Uno имеет один аппаратный последовательный порт, подключенный к порту USB. Arduino Mega имеет три дополнительных последовательных порта. 8.1. Библиотека Serial Для работы с аппаратными UART-контроллерами в Arduino существует встроенный класс serial. Он предназначен для управления обменом данными через UART. Рассмотрим основные функции класса serial. 8.1.1. Функция Serial.begin Синтаксис функции: Serial.begin(speed) Функция разрешает работу порта UART и — через параметр speed — задает скорость обмена в бодах (бит/с). Для задания скорости передачи данных рекомендуется использовать стандартные значения: 4800,9600, 19 200, 38 400, 57 600, 115 200 бод. Например: Serial.begin(9600); // инициализация порта, скорость 9600 бод
Глава 8. Arduino и последовательный порт UART 141_ 8.1.2. Функция Serial.print Синтаксис функции: Serial.print (val), Serial.print (val, format) Функция передает данные через последовательный порт как ASCII-текст и может принимать различные типы данных. Так, целые числа выводятся соответствующими им символами ASCII, а вещественные — с помощью двух ASCII-символов: для целой и дробной частей. Байты передаются как символ с соответствующим номером. Символы и строки отсылаются как есть. Параметры функции: ? val — данные для передачи через последовательное соединение; ? format — базис для целых чисел или количество знаков после запятой для вещественных: • byte — выводит число в виде байта; • bin — выводит число в двоичном формате; • ост — выводит число в восьмеричном формате; • dec — выводит число в шестнадцатеричном формате; • hex — выводит число в десятичном формате. 8.1.3. Функция Serial.println Синтаксис функции: Serial .println () Serial. println (val) Serial.println (val, format) Функция выводит данные через последовательный порт UART в виде ASCII- символов с добавлением символов переноса строки (\г, код 13) и (\п, код 10), чтобы следующее сообщение отображалось с новой строки. В остальном она аналогична функции print (). Параметры функции: ? val — данные для передачи через последовательное соединение; ? format — базис для целых чисел или количество знаков после запятой для вещественных. 8.1.4. Функция Serial.write Синтаксис функции: Serial. write (val) Функция выводит двоичные данные через последовательный порт UART и возвращает количество переданных байтов.
142 Часть III. Практическое применение Aftiuk Параметр функции: vai— данные для передачи через последовательное соеди нение. 8.1.5. Функция Serial.available Синтаксис функции: Serial.available() Функция возвращает количество байтов, принятых последовательным портом и записанных в буфер. Буфер последовательного порта может хранить до 64 байтов В случае пустого буфера возвращает 0. Пример: п* Serial, available(); // в п - число принятых байтов 8.1.6. Функция Serial.read Синтаксис функции: Serial.read() Функция возвращает очередной байт из буфера последовательного порта. Если буфер пуст — возвращает число 1 (Oxffff). Пример: char var= Serial.read(); // чтение байта из буфера 8.2. Использование UART для отладки программ Среда Arduino ГОЕ не содержит отладчика, что создает проблемы в поиске ошибок кода программы. А, как известно, без ошибок программы сразу не пишутся. Формальные ошибки выявляются при компиляции, а с алгоритмическими и вычислительными сложнее. И основная функция отладки — увидеть состояние программы и узнать значение переменных. Это можно сделать, передав нужную информацию на компьютер через последовательный интерфейс. Среда Arduino ГОЕ включает монитор последовательного порта, позволяющий получать и посылать данные обмена с платой. 8.2.1. Подключение к плате Arduino нескольких кнопок Рассмотрим, как использовать отладочную информацию в проекте подключения к плате Arduino нескольких кнопок. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB;
Глава 8. Arduino и последовательный порт UART 143_ О плата прототипирования; О кнопка — 6 шт.; ? резисторы 10 кОм — 6 шт.; О соединительные провода. В представленной на рис. 8.1 монтажной схеме этого проекта шесть кнопок подключены к выводам контроллера Arduino. При определении нажатия мы будем выводить в последовательный порт номер нажатой кнопки и контакт (пин) ее подключения. Создадим в Arduino IDE новый скетч, занесем в него код из листинга 8.1, загрузим скетч в плату Arduino и откроем монитор последовательного порта. При нажатии на кнопку в порт должна выводиться информация о номере нажатой кнопки и контакте (пине) ее подключения (рис. 8.2). Если при нажатии кнопки такая информация не выводится или выводится неверная, ищем ошибку или в подключении, или в коде программы. // кнопки выбора режима int pinButtons[]={5,6,7,8,9,10}; int lastButtons[]={0,0,0,0,0,0,0,0}; int currentButtons[]={0,0,0,0,0,0,0,0}; int countButtons=6; void setup(void) { // запуск последовательного порта Serial.begin(9600); void loop(void) { // проверка нажатия кнопок for(int i=0;i<countButtons;i++) { currentButtons[i] = debounce(lastButtons[i],pinButtons[i]); if (lastButtons[i] == 0 && currentButtons[i] == 1) { // если нажатие. // вывод номера нажатой кнопки и пина подключения Serial.print("button=");Serial.print(i); Serial.print(" pin - ");Serial.println(pinButtons[i]); } lastButtons[i] = currentButtons[i1; // Функция сглаживания дребезга // Принимает в качестве аргумента предыдущее состояние кнопки, // выдает фактическое int debounce(int last,int pinl) { int current = digitalRead(pinl); // Считать состояние кнопки
144 Часть III. Практическое применение Arduino Рис. 8.1. Монтажная схема подключения шести кнопок Рис. 8.2. Вывод отладочной информации в монитор последовательного порта
Глава 8. Arduino и последовательный порт UART 145 if (last != current) // если изменилось... { delayE); // ждем 5 мс current = digitalRead(pinl); // считываем состояние кнопки return current; // возвращаем состояние кнопки Электронный архив Полный вариант рассмотренного скетча находится в папке examples\8\_08_01 сопровождающего книгу электронного архива (см. приложение 2). 8.3. Использование UART для установки параметров При написании скетчей часто используют начальную установку параметров. Но если необходимо внести в скетч изменения, приходится его потом заново компилировать и загружать в плату, что не всегда удобно или даже возможно. Поэтому нужно предусмотреть интерфейс для установки параметров без необходимости вносить изменения в программу. Один из вариантов — получение параметров программы по последовательному порту и хранение их в памяти EEPROM. Микроконтроллеры ATmega имеют на борту энергонезависимую память EEPROM, не поте- ряющую записанные в нее данные даже после отключения питания. 512 байтов такой памяти несут ATmega8 и ATmegal68, 1024 байта— ATmega328, 4096 байтов — Arduino Mega. Память типа EEPROM допускает несколько десятков тысяч циклов записи и стирания данных. Для работы с этой памятью в составе Arduino IDE имеется удобная библиотека EEPROM. Библиотека содержит две функции: чтения и записи в память данных. Функция eeprom. read считывает байт из энергонезависимой памяти EEPROM. Если байт до этого никогда не перезаписывался — вернет значение 255. Синтаксис функции: EEPROM. read (address) Параметр: address— порядковый номер ячейки памяти для чтения от 0 до 512 (или 1024) (int); Возвращаемое значение — байт, хранимый в ячейке памяти. Функция eeprom.write записывает байт в энергонезависимую память. Синтаксис функции: EEPROM.write(address, value) Параметры: ? address — порядковый номер ячейки памяти для записи — от 0 до 511 (int); ? value — байт для записи — от 0 до 255 (byte). Возвращаемого значения нет.
146 Часть III. Практическое применение Arduino Теперь рассмотрим получение данных платой Arduino по последовательному порту. Мы уже знаем, что функция serial.available о позволяет проверить, можно ли прочитать данные из последовательного порта. Arduino имеет 64-байтовый буфер последовательного порта. Функция вызывается без параметров и возвращает количество доступных для чтения байтов. if(Serial.available()) { // выполнить, если имеются данные } Итак, напишем скетч — шаблон для получения и установки параметров по последовательному порту. Данные будем отправлять в формате: <байт начала передачи><параметр>=<значение><0айт конца передачи> Например: *<параметр>=<значение>$ Функция seriaiEvent () вызывается, если в буфере последовательного порта есть какие-либо данные. Побайтно получаемые данные записываем в строку inputstring. При получении байта конца передачи устанавливаем признак конца получаемых данных: stringComplete = true; И начинаем парсинг полученных данных и запись данных для параметра в соответствующие ячейки EEPROM. Для записи числа int необходимо использовать два байта памяти EEPROM: void save_param2_EEPR0M(int addr,int val) { EEPROM.write(addrfhighByte(val)); EEPROM.write(addr+1,lowByte(val)); } Содержимое скетча представлено в листинге 8.2. // подключение библиотеки EEPRQM #include <EEPROM.h> // данные, пришедшие из последовательного порта String inputstring = ""; // строка пришла boolean stringComplete = false; //*** данные от компа // команда int params[]={1,2,3,4,5}; int tekparam;
Глава 8. Arduino и последовательный порт UART 147_ II данные int paramsdata[5] = {0,О,О, 0,0}; int tekparamsdata; // адреса в EEPROM для параметров int addrEEPRQM[] = {0,2, 4,6,8}; void setup () { // запуск последовательного порта Serial.begin(9600); // резервирование 30 bytes для inputString: inputString.reserveC0); // загрузка параметров программы из EEPROM void loop () { // проверка прихода строки из последовательного порта if (stringComplete) { Serial.println(inputString); // парсинг строки if(parse_string()) { set_param(); // установка параметра } // очистить строку inputString = ""; stringComplete = false; // при наличии данных в буфере последовательного порта void serialEvent() { boolean flagl=false; while (Serial.available() && flagl==false) { // получить байт: char inChar = (char)Serial.read(); // если байт конца передачи if (inChar == f$f) { stringComplete = true; flagl=true; } else // добавить в inputString: inputString += inChar; // парсинг получаемых данных boolean parse_string() {
148 Часть III. Практическое применение Arduino int sl,s2; int lengthl==inputString. length () ; Serial.print("str=");Serial.println(inputString); Serial.print("lengthl=");Serial.println(lengthl); if(inputString.charAt(O)!='*') return false; if(inputString.charAt(lengthl-1)!=f;f) return false; // for(int i=l;i<lengthl;i++) { if(inputString.charAt(i)==';f) {sl=i;break;} } tekparam=inputString.substringA,si).tolnt(); // action for(int i=sl+l;i<lengthl;i++) { if(inputString.charAt(i)==f; ') {s2=i;break;} } tekparamsdata=inputString.substring(s1+1,s2).tolnt(); Serial.print("tekparam=");Serial.printIn(tekparam); Serial.print("tekparamsdata=");Serial.printIn(tekparamsdata); return true; } // установка параметра void set_param() { if(tekparam>=0 && tekparam<5) { paramsdata[tekparam]=tekparamsdata; save_param2_EEPROM(addrEEPROM[tekparain],tekparamsdata); Serial.println("param set!!!"); } else { Serial.printIn("Wrong param!!!"); // сохранить значения параметра в EEPROM void save_j>aram2_EEPR0M(int addr,int val) { EEPROM.write(addr,highByte(val)); EEPROM.write(addr+1,lowByte(val)); . Электронный архив Полный вариант рассмотренного скетча находится в папке examples\8\J)8JJ сопровождающего книгу электронного архива (см. приложение 2).
Глава 8. Arduino и последовательный порт UART 149^ Загрузим этот скетч в плату Arduino и попробуем отправлять в монитор последовательного порта команды установки параметров. При этом выводим в него и отладочную информацию (рис. 8.3). 1*2;1342; !atr-*2;1342; ;lengthl-8 tekpara»«2 : teiparaaedata«1342 pare» set!!! Рис. 8.З. Отправка параметров из последовательного порта в Arduino 8.4. Библиотека SoftwareSerial Библиотека SoftwareSerial дает возможность реализовать последовательный интерфейс на любых цифровых выводах Arduino с помощью программных средств, дублирующих функциональность UART (отсюда и название SoftwareSerial). Библиотека позволяет программно создавать несколько последовательных портов, работающих на скорости до 115 200 бод. При использовании нескольких последовательных портов в каждый момент времени только один из них может получать данные. На платах Arduino Mega и Arduino Mega2560 некоторые выводы не поддерживают прерывания, возникающие при изменении уровня сигнала. В силу этого, на этих платах в качестве вывода RX могут использоваться только следующие выводы: 10, И, 12, 13, 14, 15, 50, 51, 52, 53, А8 F2), А9 F3), А10 F4), АН F5), А12 F6), А13F7),А14F8),А15F9). На Arduino Leonardo некоторые выводы также не поддерживают прерывания, возникающие при изменении уровня сигнала. Поэтому на ней в качестве вывода RX
150 Часть III. Практическое применение Arduino могут использоваться только следующие выводы: 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16(MOSI). 8.5. Соединение по UART двух плат Arduino Соединим две платы Arduino и установим связь между ними по программному последовательному порту UART. Монтажная схема этого проекта показана на рис. 8.4. Рис. 8.4. Монтажная схема соединения двух плат Arduino по программному последовательному порту Загрузим на каждую плату Arduino скетч из листинга 8.3, который поможет нам из монитора последовательного порта Arduino ШЕ отправлять данные в другую плату Arduino, подключим каждую плату по USB к компьютеру и проверим обмен сообщениями: на рис. 8.5 показан процесс отправки данных из одной платы Arduino в другую, а на рис. 8.6 — прием этих данных другой платой. // подключение библиотеки SoftwareSerial #include <SoftwareSerial.h> // создание экземпляра SoftwareSerial // на RX-8, TX-9 SoftwareSerial mySerial(8, 9); void setup() { // Инициализируем последовательный порт Serial.begin(9600) ; Serial.println("I'm ready!");
Глава 8. Arduino и последовательный порт UART 151 Рис. 8.5. Отправка данных из одной платы Arduino в другую Рис. 8.6. Прием платой Arduino данных, посланных в нее из другой платы
152 Часть III. Практическое применение Arduino // устанавливаем скорость передачи данных // для последовательного порта, созданного // библиотекой SoftwareSerial mySerial.begin(9600); mySerial.println("Hello, world?"); void loopO { // получаем по Software - отправляем в монитор if (mySerial.available()) Serial.write(mySerial.read()); // пишем в монитор (аппаратный) - отправляем по Software if (Serial.available()) mySerial.write(Serial.read()); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\8\_08_03 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 9 Подключение датчиков к плате Arduino Датчик, или сенсор, — это устройство, с помощью которого мы измеряем значение какого-либо технологического параметра. Датчики позволяют определять, что происходит во внешней среде, и действовать на основе этой информации. Датчики, наверное, можно назвать органами чувств системы. Любой датчик состоит из чувствительного элемента и преобразовательной системы, выполняющей преобразование входного воздействия любой физической величины в сигнал, удобный для дальнейшего использования. 9.1. Подключение аналоговых датчиков Самый простой тип датчиков — аналоговые датчики. Это первичные преобразователи. Такой тип датчиков применяется в системах непрерывного измерения и регулирования. Принцип действия аналоговых датчиков состоит в том, что при изменении измеряемого параметра происходит соответствующее изменение его выходного сигнала. Выходное напряжение может принимать значения от О В до напряжения питания, хотя обычно рабочий диапазон напряжений более узкий. Примеры датчиков: ? акселерометры — для обнаружения наклона (используются в смартфонах и планшетах); ? магнитометры — для обнаружения магнитных полей (используются при создании цифровых компасов); ? инфракрасные датчики — для определения расстояния до объекта; ? датчики температуры — для определения температуры; Q фоторезисторы — для измерения освещенности. Между измеряемой датчиком величиной и возвращаемым напряжением устанавливается определенная зависимость. Например, чем больше величина, тем больше напряжение, или наоборот, чем больше величина, тем напряжение меньше.
154 Часть III. Практическое применение Arduino 9.1.1. Подключение к плате Arduino аналогового датчика температуры LM335 Рассмотрим работу с аналоговыми датчиками температуры на примере датчика LM335 — недорогого температурного чувствительного элемента с диапазоном от -40 до +100°С и точностью в 1°С ( рис. 9.1). По принципу действия датчик LM335 представляет собой стабилитрон, у которого напряжение стабилизации зависит от температуры. При повышении температуры на один градус Кельвина напряжение стабилизации увеличивается на 10 мВ. Типовая схема включения (соответствует типовой схеме включения стабилитрона) показана на рис. 9.2. /\ —V- (GND) ч V+ ADJ Рис. 9.1. Датчик температуры LM335 Рис. 9.2. Типовая схема включения датчика LM335 При взгляде на эту схему сразу можно спросить, каково же сопротивление резистора R1 и какое напряжение питания должно быть при такой схеме включения? Ответ содержится в технической документации (Data Sheet), где сказано, что нормальная работа изделия гарантируется в диапазоне токов 0,45...5,00 мА при сопротивлении резистора R1 2,2 кОм. Следует заметить, что предел в 5 мА превышать не следует, поскольку датчик будет перегреваться и измерять при этом собственную температуру. Электронный архив Техническая документация производителей (Data Sheet) практически на все компоненты и устройства, задействованные в проектах этой книги, находится в каталоге datasheets сопровождающего книгу электронного архива (см. приложение 2). Согласно документации датчик проградуирован по абсолютной шкале Кельвина. При температуре -273,15°С, а это абсолютный ноль по Кельвину, рассматриваемый датчик должен показать нулевое напряжение. При увеличении температуры на каждый градус выходное напряжение стабилитрона будет возрастать на целых 10 мВ, или на 0,010 В. Температура 25°С — единственная точка калибровки сенсора. При этой температуре на выходе датчика должно быть: 298,15 х 0,010 = 2,9815 В. Для калибровки датчика используется схема, представленная на рис. 9.3.
Глава 9. Подключение датчиков к плате Arduino 155 ¦Опит Выход 10 мВ^К LM335 Рис. 9.3. Схема калибровки датчика LM335 Итак, подключаем датчик температуры LM335 по схеме, представленной на рис. 9.4, и пишем скетч (листинг 9.1) считывания данных с датчика и вывода показаний в последовательный порт (рис. 9.5). . int lm335=0; // подключение датчика к аналоговому входу АО void setup () { Serial.begin(9600) ; } void loop () { double val = analogRead(lm335); // чтение double voltage = val*5.0/1024; // перевод в значение в вольтах double temp = voltage*100 - 273.15; // в градусы Цельсия Serial.print(" temp = "); Serial.printIn(temp); delayA000); Рис. 9.4. Монтажная схема подключения датчика LM335 к плате Arduino
156 Часть III. Практическое применение Arduino tei/ teup te«p teip teip teip teip teip teip temp teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip teip - 25.68 - 26.17 - 25.68 - 25.68 - 25 68 - 26 17 - 25 68 - 25.68 - 25.68 - 25.68 - 25.19 « 26 17 - 26.65 - 27.14 - 26.65 - 27 63 - 28.12 - 28 61 - 29.10 - 30 07 - 31.05 - 30.07 - 29 10 - 28.61 - 28 12 - 27.63 - 27.14 - 26.65 - 26.17 Рис. 9.5. Вывод результатов измерения данных датчиком LM335 в монитор последовательного порта Электронный архив Полный вариант рассмотренного скетча находится в папке examples\9\J>9JI сопровождающего книгу электронного архива (см. приложение 2). 9.2. Подключение датчиков по протоколу 1-Wire Интерфейс 1-Wire разработан фирмой Dallas Semiconductor (ныне MAXIM) в конце 1990-х гг. Этот интерфейс интересен тем, что для двустороннего обмена требуется всего одна линия. Правда, еще понадобится общий провод («земля») и провод питания (не всегда). Причем на эту одну линию можно «повесить» несколько устройств. Протокол очень прост и легко реализуется на микроконтроллере программно. Для взаимодействия Arduino с устройствами 1-Wire необходимо установить библиотеку One Wire. Электронный архив Библиотека OneWire размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
Глава 9. Подключение датчиков к плате Arduino 157 9.2.1. Подключение к плате Arduino цифрового датчика температуры DS18B20 Рассмотрим подключение к Arduino цифровых датчиков температуры DS18B20, работающих по протоколу 1-Wire. DS18B20 (рис. 9.6)— цифровой термометр с программируемым разрешением от 9 до 12 битов, которое может сохраняться в памяти EEPROM прибора. DS18B20 обменивается данными по шине 1-Wire и при этом может быть как единственным устройством на линии, так и работать в группе. Все процессы на шине управляются центральным микропроцессором. (BOTTOM VIEW) ТО-92 (DS18B20) Рис. 9.6. Датчик DS18B20 Диапазон измерений датчика: от -55 до +125°С, с точностью 0,5°С в диапазоне от -10 до +85°С. Датчик может запитываться двумя способами: внешним питанием (три провода) или паразитным (питание от шины, 2 провода). Монтажная схема подключения нескольких (в нашем случае — двух) датчиков температуры DS18B20 от внешнего питания к Arduino представлена на рис. 9.7. Каждый датчик типа DS18B20 имеет уникальный 64-битный последовательный код, который позволяет общаться с множеством датчиков DS18B20, установленных на одной шине. Первые 8 битов — код серии (для DS18B20 — 28h), затем 48 битов уникального номера и в конце 8 битов CRC-кода. Такой принцип позволяет использовать один микропроцессор, чтобы контролировать множество датчиков DS18B20, распределенных по большому участку. Скетч для получения датчиков DS18B20, подключенных к шине 1-Wire, и вывода их уникальных идентификаторов в последовательный порт представлен в листинге 9.2.
158 Часть III. Практическое применение Arduino Рис. 9.7. Монтажная схема подключения нескольких датчиков DS18B20 к плате Arduino // Подключение библиотеки OneWire #include «OneWire.h> // пин подключения датчиков OneWire ds(8); void setup(void) { // запуск последовательного порта Serial.begin(9600) ; void loop(void) { byte i; byte type_s; byte addr[8]; // поиск устройств if ( !ds.search(addr)) { Serial.println("No more addresses."); Serial.println(); ds.reset_search(); delayB50); return;
Глава 9. Подключение датчиков к плате Arduino 159 II вывод уникального номера Serial.print("ROM ="); for( i = 0; i < 8; i++) { Serial.write(' f); Serial.print(addr[i], HEX); if (OneWire::crc8(addr, 7) !=addr[7]) { Serial.println("CRC is not valid!"); return; } Serial. println () ; // определение типа датчика switch (addr[0]) { case 0x10: Serial.println(" Chip = DS18S20"); type_s = 1; break; case 0x28: Serial.printIn(" Chip = DS18B20"); type_s = 0; break; case 0x22: Serial.println(" Chip = DS1822"); type_s =0; break; default: Serial.println("Device is not a DS18x20 family device."); return; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\9\_09_02 сопровождающего книгу электронного архива (см. приложение 2). Загружаем этот скетч в плату Arduino, запускаем монитор последовательного порта и наблюдаем вывод в последовательный порт данных уникального идентификатора для каждого датчика (ррс. 9.8). Данные о температуре хранятся в оперативной памяти датчика (рис. 9.9). Память состоит из оперативной ROM и энергонезависимой EEPROM: О первые два байта содержат данные об измеренной температуре; П третий и четвертый байты хранят верхний (тн) и нижний (tl) пределы температуры; ? пятый и шестой — не используются;
160 Часть III. Практическое применение Arduino П седьмой и восьмой— байты-счетчики. Они могут использоваться для более точного измерения температуры; ? девятый байт хранит CRC-код предыдущих восьми. Рис. 9.8. Вывод идентификаторов двух датчиков DS18B2Q в монитор последовательного порта SCRATCHPAD (Power-up State) byte 0 bytel byte 2 byte3 byte 4 byte 5 byte 6 byte 7 byte 8 Temperature LSB E0h) 1 ^^ Temperature MSB @5h) J v" ' Th Register or User Byte 1 ¦ TL Register or User Byte 2* Configuration Register41 Reserved (FFh) Reserved (OCh) Reserved (lOh) CRC* EEPROM M Bi <« > TH Register or User Byte 1 Tl Register or User Byte 2 Configuration Register "Состояние после вилку зависит от значений, сохраненного в EEPROM Рис. 9.9. Карта памяти датчика DS18B20 Последовательность команд для получения от датчика данных о температуре должна быть такой: 1. Произвести reset и поиск устройств на линии 1-Wire. 2. Выдать команду 0x44, чтобы запустить конвертацию температуры датчиком.
Глава 9. Подключение датчиков кллате Arduino 161_ 3. Подождать не менее 750 мс. 4. Выдать команду Охве, чтобы считать ОЗУ датчика (данные о температуре будут в первых двух байтах). Скетч получения данных с датчиков температуры DS18B20 и вывода данных в последовательный порт представлен в листинге 9.3. // Подключение библиотеки OneWire tinclude <OneWire.h> // пин подключения датчиков OneWire ds(8); void setup (void) { // запуск последовательного порта Serial.begin(9600); void loop (void) { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]; float temperature; // поиск устройств if ( !ds.search(addr)) { Serial.println("No more addresses."); Serial.println(); ds.reset_search(); delayB50); return; // вывод уникального номера Serial.print("ROM ="); for( i = 0; i < 8; i++) { Serial.write(f f); Serial.print(addr[i], HEX); if (OneWire::crc8(addr, 7) !=addr[7]) { Serial.println("CRC is not valid!"); return;
162 Част» Ш. Практическое пришенениеАп}^ Serial-print in О ; // switch case Qxlt): Serlal.printlnC Chip = DS18S20-1 ; typejs = 1; breaks case 0x28: Serial-println Г Oiip = Ш18В20-); types = 0; break; case 0x22: Serial.primtln I* Chip = В&1Ш22т); type_s = 0; break; defaailt: Serial, printlm I "Device is n*ot а Ш1Вж20 family tteryice'. return; I // сброс иины ds. reset О; // яидг^яятнидр™^ алреса |уникашьного немэра;) ds.selectCaddrl; ds.write!0x44, 1); // пауза > 750 шве // сброс ни present: — ds. reset О; // иыегашяезте адреса Сушшшньюоро номера) ds. select faddrj; // комацда чтения памяти дщатчика ds.Mrite(OxBE) ; Serial, print Cw Data = m}s Serial.printCpxesent, ШХ1); Serial-print!" "I; // чтение памяти датчика, 9 байт for С i = 0; i < 9; inn-J f datafi] = ds.readO; Serial .print Idatalil, EEXD; Serial.printr "»; } Serial.print Iя* CRG="|; Serial.printCQneHire::crc8|datar 8b JBEX1; Serial.println О ;
Подключение датчиков к плжпеАпкшю 163 // перевод тъмтах в зшадогаие температуры tenperature= ((float) |Kiuit)dataI©l 1 UCintJdatalU) « 8И * 0.0625 hi- 0.03125; Serial, prints" Temperature = "Э; Serial.i ЭЛЕКТРОННЫМ ARXMB Полный вариант раоснклренного сверча камодится в палое ежаячр18я«_09_вЗ ясвэммивго вни^^ дпмгпиунноур авмива нзи. прияояввние 2ж» Загр\ ~жаем этот сжетя в плату Aiduina, запускаем монитор последовательного порп н наблюдаем вывод показаний датчиков температуры DS1SB20 в ныи порт (рис 9.10). iOS1SB20bi 9.3. Подключение датчиков влажности и температуры DHT Датчики DHT11 и DHT22 (рис. 9.11) состоят из емжосттюго, термистора. Кроме тога, дагчижи содержат чип, который преобразует сигнал в цифровой. В процессе производства | DHT, могут получать разные параметры, и чтобы вильны м и. производитель калибрует каждый дагак DHT в i
164 Часть III. Практическое применение Arduino а поправочный коэффициент сохраняется в памяти и вызывается при считывании данных. Общие характеристики датчиков: ? DHT11: • очень низкая стоимость; • питание и I/O: 3-5 В; • определение влажности 20-80% с точностью 5%; • определение температуры 0...50°С с точностью 2%; • частота опроса не более 1 Гц (не более одного раза в 1 с); • размеры 15,5x12x5,5 мм; ? DHT22: • низкая стоимость; • питание и I/O: 3-5 В; • определение влажности 0-100% с точностью 2-5%; • определение температуры -4О...+125°С с точностью ±0,5°С; • частота опроса не более 0,5 Гц (не более одного раза в 2 с); • размеры 15,1x25x7,7 мм. Сенсор DHT22 имеет лучшие, чем у DHT11, характеристики, но более высокую стоимость. Рис. 9.11. Датчики DHT11 (слева) и DHT22 (справа) Преимущество этих датчиков— небольшие размеры, низкое энергопотребление, высокая дальность передачи (до 20 м), к недостаткам можно отнести относительно невысокую точность измерений. Датчики имеют 4 вывода стандарта 2,54 мм: ? 1— VCC (гаггание 3-5 В); ? 2 — DATA (вывод данных); ? 3 — не используется; ? 4 — GND («земля»).
Глава 9. Подключение датчиков к плате Arduino 165^ Протокол обмена— однопроводный, по структуре весьма похожий на протокол датчиков DS18В20, но с различиями: ? DHT не умеет работать в «паразитном» режиме (при питании по линии данных); О каждый DS18B20 имеет персональный идентификатор, что дает возможность подключения нескольких таких датчиков к одному контакту (пину) Arduino. Однако у DHT такой возможности нет— один датчик будет использовать строго один цифровой пин. Чтобы начать получать данные с датчика, микроконтроллер должен отправить датчику запрос и дождаться от него ответа, что датчик готов передавать информацию. Происходит это следующим образом. Когда микроконтроллер собрался принимать данные, он должен притянуть линию данных к нулю на 18 мс, после чего отпустить ее обратно к 1. При этом микроконтроллер переходит в режим ожидания и следит за тем, что происходит с линией данных. Через 20-40 мкс, если все в порядке, датчик отвечает притягиванием уже со своей стороны линии данных к нулю на 80 мкс и отпускает ее на 80 мкс. Таким образом датчик дает понять микроконтроллеру, что с ним все в порядке и он начинает передачу данных. Данные передаются побитно. Перед передачей каждого нового бита датчик притягивает линию данных к нулю на 50 мкс, после чего отпускает ее к 1. В зависимости от того, на сколько микросекунд датчик отпускает линию к 1, микроконтроллер распознает, какой бит был передан: если длительность временного промежутка до следующего притягивания к нулю 26-28 мкс, то передан 0, если 70 мкс — передана 1. Количество передаваемой информации — 40 битов E байтов). Первый и второй байты содержат целую и дробную части информации о влажности, третий и четвертый байты содержат целую и дробную части информации о температуре, пятый байт — контрольная сумма, которая представляет собой последние 8 битов от сложения предыдущих 4 байтов. После передачи пакета данных датчик переходит в спящий режим до следующего запроса со стороны микроконтроллера. Монтажная схема подключения датчика DHT22 к плате Arduino показана на рис. 9.12. Для считывания данных датчика DTH22 с помощью Arduino существует готовая библиотека DHT, которую необходимо импортировать в Arduino IDE. Электронный архив Библиотека DHT размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Итак, загружаем в плату Arduino скетч получения данных с датчика DHT22 и вывода их в последовательный порт Arduino (листинг 9.4), запускаем монитор последовательного порта и наблюдаем вывод в него значений влажности и температуры (рис. 9.13).
166 Часть III. Практическое применение Arduino •шйшашшяадмшшмю^^ Рис 9.12. Монтажная схема подключения датчика DHT22 к плате AnJuino Рис 9.13. Вывод данных влажности и температуры с датчика DHT22
Гпава 9. Подключение датчиков к плате Алклпо 167 II подключение библиотеки ЕГГН ¦include "DHT.h" // пин подключения контакта DATA tdefine DHTPIN 10 // тип датчика - DHT 22 ¦define DHTTYPE DHT22 // создание экземпляра объекта DHT DHT dht (DHTPIN, DHTTYPE) ; void setup () { // запуск последовательного порта Serial.begin (9600); // запуск DHT dht.begin(); void loop() { // получение ^нш« с датчика int h = dht.readHumidityO; int t = dht.readTenperatureO; Serial. print ("Humidity^"); Serial .print (h); Seria 1. print (" Temperature=") ; Ser ial. print In (t) ; // пауза (измерения не чаще одного раза в 2 с) delayB000); Электронным архив Полный вариант рассмотренного скетча находится в папке examples№\J)9jD4 сопровождающего книгу электронного архива (см. приложение 2). 9.4. Подключение датчиков по протоколу 12С Последовательный протокол обмена данными t*C использует для передачи данных две двунаправленные линии связи: ? SDA (Serial Data) — шина последовательных данных; ? SCL (Serial Clock) — шина тактирования (синхронизации). Задействуются также две линии для питания. Шины SDA и SCL подтягиваются к шине питания через резисторы. При работе по протоколу 12С в сети должно присутствовать хотя бы одно ведущее устройство (master), которое инициализирует передачу данных и генерирует <
168 Часть III. Практическое применение Arduino налы синхронизации, а также ведомые устройства (slave), которые передают данные по запросу ведущего. У каждого ведомого устройства есть уникальный адрес, по которому ведущий и обращается к нему. К одной шине 12С может быть подклю чено до 127 устройств, в том числе несколько ведущих. Arduino использует для работы по интерфейсу 12С два контакта. Каждая версия Arduino имеет свои выводы 12С (табл. 9.1). Таблица 9.1. Контакты 12С для разных плат Arduino Плата Arduino Arduino Uno, Nano Arduino Mega Arduino Leonardo Arduino Due Вывод SDA 4 20 2 20 Вывод SCL 5 21 3 21 Для обмена данными с устройствами по шине 12С в Arduino есть стандартная библиотека Wire. Подключим к плате Arduino несколько датчиков, работающих по протоколу 12С, и загрузим в плату Arduino скетч поиска устройств, подключенных к шине 12С загрузим в плату Arduino скетч поиска устройств, подключенных к шине 1С (листинг 9.5). Этот скетч сканирует шину 12С и выводит в последовательный порт Arduino таблицу с адресами подключенных устройств. #include "Wire.h" void setup() { Wire.begin(); Serial.begin(9600); // запуск последовательного порта void loop () { int devices; byte err, add; Serial.println("Start scan I2C bus..."); devices =0; Serial.print(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E OF"); for(add = 0; addr<= 127; addr++ ) { if((addr% 0x10) = 0) { Serial. println ()' ; if(addr< 16) Serial.print('0f) ; Serial.print(addr, 16); Serial.print(" ") ;
Глава 9. Подключение датчиков к плате Arduino 169 Wire.beginTransmission(addr);err = Wire.endTransmission(); if (err == 0) { if (addr<16) Serial.print("); Serial.print(addr, HEX); .devices**; } else { Serial.print ("—"); } Serial.print(" "); delayA); } Serial .println () ; if (nDevices = 0) Serial.printIn("No I2C devices found\n"); delayB500) ; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\9\_09_Q5 сопровождающего книгу электронного архива (см. приложение 2). 9.4.1. Подключение к плате Arduino датчика интенсивности света ВН1750 Рассмотрим подключение к плате Arduino датчика интенсивности света — модуля GY-302 на базе микросхемы ВН1750 (рис. 9.14), работающего по протоколу 12С. Рис. 9.14. Модуль GY-302 на базе микросхемы ВН1750
170 Часть III. Практическое применение Arduh Измерение освещенности является важным параметром при создании приложении домашней автоматики. Освещенность измеряют в люксах Aх). Люкс равен освещенности поверхности площадью 1 м2 при световом потоке падающего на нее излучения, равном 1 люмену (лм). Модуль GY-302 на базе чипа ВН1750 представляет собой высокоточный цифровой датчик интенсивности света, выдающий значение в люксах. Модуль имеет S выводов: П VCC — питание 5 В; ? GND — «жиля»; ? SDA—данные 12С; ? SCL— синхронизация 12С; П ADDR — выбор адреса для протокола 12С. Разберемся с возможными адресами датчика BH17S0. Существуют два варианта подключения датчика BH17S0 к шине 12С (рис. 9.15). Рис. 9l15u Монтажные ооемы подключения датчика ВН1750 к Arduino Загруженный на плату Aidoino скетч из листинга 9.S будет сканировать шину 12С и выводить в последовательный порт Anhiino адрес подключенного устройства. Как показано на рис. 9.16, модуль BH17S0 может иметь— в зависимости от уровня сигнала на входе ADDR — два адреса @x23 и 0х5с). Это значит, что к одной плате Aiduino можно подсоединить одновременно два датчика ВН1750. Для работы Aiduino с датчиками BH17S0 написано несколько библиотек, мы здесь воспользуемся одной из них— BH17S0FVI (ее можно скачать по ссылке: https^/g^HlM»m/enjoyKwermg/BH1750FV1). Эта библиотека поддерживает все режимы датчика ВЫ 1750, позволяет производить измерения освещенности с несколькими параметрами чувствительности @,45-3,68) и разрешающей способности
Глава 9. Подключение датчиков if плате Arduino 171 AOOR-HSGH Until Рис 9.16. Адреса датчика ВН1750 Рис 9.17. Пример вывода данных с датчика ВН1750 в монитор последовательного порта Arduino при различных режимах измерении
172 Часть III. Практическое применение Arduino @,5-4 1х), а также в режиме энергосбережения. К библиотеке прилагается пример (BH1750FVI_Demo) вывода в последовательный порт Arduino данных с датчика ВН1750 при различных режимах измерения (рис. 9.17). Электронный архив Библиотека BH1750FVI размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). В следующих главах мы рассмотрим и другие датчики, работающие по протоколу 12С.
ГЛАВА 10 Использование дисплеев в проектах Arduino Данные, получаемые с датчиков, в предыдущей главе мы выводили в монитор по: следовательного порта Arduino. Смотреть на показания датчиков через последовательный порт не всегда приемлемо — и нам необходимы более удобные устройства для отображения данных. Электронные устройства, предназначенные для визуального отображения цифровой, цифро-буквенной или графической информации, называются дисплеями. И в этой главе мы рассмотрим работу Arduino с различными дисплеями. 10.1. Символьные дисплеи на микроконтроллере HD44780 Жидкокристаллические (ЖК) дисплеи на основе микроконтроллера HD44780 наиболее часто используются в проектах Arduino. Такой дисплей представляет собой модуль, состоящий из микроконтроллера HD44780 и непосредственно самого ЖК- дисплея. Микроконтроллер принимает команды й отрисовывает на ЖК-дисплее соответствующие символы. Существует огромное количество разновидностей таких ЖК-модулей: они могут содержать 1, 2 или 4 строки с различным числом символов в строке, быть с подсветкой или без оной, а также с различным цветом подсветки. Объединяет их всех наличие контроллера HD44780, знание команд которого позволит без проблем использовать в своих проектах ту или иную модификацию этого модуля. ЖК-дисплеи Winstar WH1602 (рис. 10.1) на контроллере HD44780 имеют 16 выводов, назначение которых представлено в табл. 10.1. Таблица,10.1. Назначение выводов дисплея WH1602 № вывода 1 2 3 Название Vss Vdd Vo Функция Общий (GND) Напряжение питания C или 5 В) Контрастность
174 Часпт* Ш. Праюпичесюое пршленение Агвиюо г 10,1 (окончание) RS R/W 6 DBO DB1 9 10 11 \ DB2 Л DB3 Jl DB4 Л тинвшныхЗ ШШЯЯ ДЭНИпК «1 •«ядмисЧ 12 DB5 13 DB6 1Ьти»дамиых6 14 DB7 15 ' LHH Напряжение ncwsenn(^ 16 ! LED- НапрюемвподсвелиН mi. i в этом модуж организованы подача шпаннж, настрои- 1раствостнн1юдсветжа(рнс. 10^). ЖК-джшкя ^Kfinsiar нсобжздрш внсошнн источник питания 4у5—5,5 В. - и LED- служат для вкшоч^па no^caeivH. Питание к подсветке под- номинального тока с помощью внешнего резистора R. Номина шюде должно составлять 3,5 В, а ток— 40 мА. Исходя из R=E-3 Д>0,04 = 37 Ом. •зуется делитель 10-20 кОм. Перед выводом необходимо убедиться, что уцявяякяцее контрастностью напряжение
Глава 1О. Использование дисплеев в проектах Апкшю 175 + 5В находится в рабочем давшазове. Для этого с помощью потенциометра добейте чтобы верхний рад < Если после подачи питания верхний рад угольниками, можно приступать к подключеншо ЖК-дисплея к опте Апкшю. Дня управления ЖК-дисплеем необходимо 6 или 10 выводов HD4478G—в зависимости от тогот выбран 4-или S-бнтныи режим обмена, ми. Для сокращения требуемого числа выводе» микроконтроллера HD447801 работать и в 4-битном режиме. В этом случае на его выводы DB4-DB7 будут передаваться старшие четыре бита гумдгах/гамаиди а затем - тыре бита. Выводы DB0-DB3 останутся незздействованншт. Монтажная схема подключения ЖК-дисплея к шаге Апкшю предст рис. 103. Дня работы с ЖК-дисплеями на контроллере HD447S0 в Ankrino IDE встроенная библиотека LiqmdCiystaL Чтобы с ней работать, необходимо ее подключить: finclucie И Создать ЗКЭеМПЛЯр объекта LiqeidCrystai: LiqtiidCrystal lcdC5r 1. Ъш 9. W, 11>; Объект ТИПа LiquidCrysital ПрИСОЗДаН ? пин подключения контакта RS A2); ? пин подключения контакта Е A1); ? пин подключения контактаD4EX ? пин подключения контакта D5 D); О пин подключения контакта D6 (ЗХ ? пнн подключения контакта D7 B)и
176 Часть III. Практическое применение Arduino + 5В Рис. 10.3. Монтажная схема подключения ЖК-дисплея Winstar WH1602 к плате Arduino Библиотека LiquidCrystal содержит много примеров, позволяющих разобраться в ее работе. Кратко познакомимся с функциями этой библиотеки. 10.1.1. Функция beginf) Синтаксис функции: led.begin(cols, rows) Функция определяет размерность индикатора (количество символов в ширину и высоту). Параметры: ? led — переменная типа LiquidCrystal; ? cols — количество символов в строке; ? rows — количество строк. 10.1.2. Функция clearf) Синтаксис функции: led. clear ()
Глава 10. Использование дисплеев в проектах Arduino 177_ Функция очищает экран жидкокристаллического индикатора и располагает курсор в его верхнем левом углу. Параметр: led — переменная типа LiquidCrystai. 10.1.3. Функция home() Синтаксис функции: led.home () Функция располагает курсор в верхнем левом углу жидкокристаллического индикатора. Используется для определения начального положения при выводе последовательного текста на экран индикатора. Чтобы еще и очистить экран индикатора, ИСПОЛЬЗуЙТе ВМеСТО ЭТОЙ фуНКЦИИ фуНКЦИЮ clear (). Параметр: led — переменная типа LiquidCrystai. 10.1.4. Функция setCursorf) Синтаксис функции: lcd.setCursor(col, row) Функция позиционирует курсор жидкокристаллического индикатора, т. е. устанавливает положение, в котором на его экран будет выведен последующий текст. Параметры: ? led — переменная типа LiquidCrystai; ? col — номер знако-места в ряду (начиная с 0 для первого знако-места); ? row — номер ряда (начиная с 0 для первого ряда). 10.1.5. Функция writef) Синтаксис функции: led.write(data) Функция записывает символ в жидкокристаллический индикатор. Параметры: ? led — переменная типа LiquidCrystai; ? data — символ, записываемый в индикатор. 10.1.6. Функция printf) Синтаксис функции print ():. led.print(data) led.print(data, BASE) Функция печатает текст на жидкокристаллическом индикаторе.
178 Часпш. Ш. Праяпючесжое приыенение AjrkMK LiquidCrystai; Д81 печати (ТИП char, byte, lust, long НИИ string); >)— основание, по которому печатаются числа: bin дм дао- 2\ dec дм десятичных (основание 10), ост дм восьмеричных SX hex дяя пюстнздцпернчшга (основание 16). 10.1.7. Функция cursorQ led. cursor t) Функция выводит на экран жцуумр» ¦И1ммим«»ясж«гп ицдикагора курсор — подчер- кивание зиако-месга в позиции, в которую будет записан следующий < Параметр: icd- 10.1.8. Функция noCursorQ Синтаксис функции: led. noCtirsor (J Функция скрывает курсор с Параметр: led—переменная типа 10.1.9. Функция ЫткО Синтаксис функции: icd.blinkC) Функция выводит на экран жцдоокрктжшшчесхюго ицци^аюра мигающий курсор. Если она используется в комбинации с функцией cursor(>т результат будет зависеть от конкретного нццшсатора. Параметр: led—переменная типа 10.1.10. Функция noBlinkO Синтаксис функции: led.noBlinkil курсор на экране мц^ущкрт птии'нч ымп шщика- Параметр: led—переменная тнв
Пива 10. Испольэовэшю дисплеев в щюежпыж А/пЛшпо 179 10.1.11. Функция (MsplayO Синтаксис функции: led. display О Функция включает жцщщкжрнсплянческнй нццжагпр после того, как ов быя выключен функцией nooispiayo. Эта фушцш восстанавлшает текст (н курсора шгарый был на дисплее. Параметр: led—переменная типа 10.1.12. Функция noDisplayO Синтаксис фушщии: led. noDisplay { } Функция выключает *|wi'>KP"gTai*IM''rfi'Jt™ nrtu^i»*iO|> без потерн огображягмпй на нем информации. Параметр: led—переменная типа Liqaddctystai. 10.1.13. Функция scmllDisplayLettO Синтаксис функции: icd.scrollMsplayljefft A а экране ицднкашра (текст и курсор) на одно Парамеф: led—переменная типа li< 10.1.14. Функция scrollDisplayRightO Синтаксис функции: led. scxoIJLMsplayfllgibt: С } Функция прокручивает информацию на экране ицдакагора (текст и курсор) на одно Параметр: led—переменная типа 10.1.15. Функция autoscroHQ Icd.aotoscxoilС) ч^рнкция включает автюматичеез^ю шшрошарушялу лцхшна жцдтлцинс!' дшеатора и заставаяет кжтгдий вывод симвеша на экрош ицдикатора предыдущие символы на одно знако-место. Если те^тцее нащювленне вывода сим- водов слева направо (значение по умодчанмо)—экран нвдикатора прокручивается
180 Часть III. Практическое применение Arduino влево, если текущее направление вывода символов справа налево — экран индикатора прокручивается вправо. Это производит на экране жидкокристаллического индикатора эффект вывода каждого нового символа в одно и то же знако-место. Параметр: led — переменная типа LiquidCrystai. 10.1.16. Функция noAutoscrollQ Синтаксис функции: lcd.noAutoscroll() Функция выключает автоматическую прокрутку экрана жидкокристаллического индикатора. Параметр: led — переменная типа LiquidCrystai. 10.1.17. Функция leftToRight() СиНТаКСИС фуНКЦИИ lef tToRight (): led.leftTorRight() Функция устанавливает направление вывода символов на экран жидкокристаллического индикатора слева направо (значение по умолчанию). Это означает, что последующие символы, выводимые на экран индикатора, пойдут слева направо, но не повлияют на выведенный ранее текст. Параметр: led — переменная типа LiquidCrystai. 10.1.18. Функция rightToLeftO Синтаксис функции: led. rightToLeftO Функция устанавливает направление вывода символов на экран жидкокристаллического индикатора справа налево (значение по умолчанию — слева направо). Это означает, что последующие символы, выводимые на экран индикатора, пойдут справа налево, но не повлияют на выведенный ранее текст. Параметр: led — переменная типа LiquidCrystai. 10.1.19. Функция createCharQ Синтаксис функции: lcd.createChar(num, data) Функция создает пользовательский символ (глиф) для использования на жидкокристаллическом дисплее. Поддерживаются до восьми символов 5x8 пикселов (нумерация с 0 до 7). Создание каждого пользовательского символа определяется массивом из восьми байтов — один байт для каждой строки. Пять младших значащих
Глава 10. Использование дисплеев в проектах Arduino 181 битов каждого байта определяют пикселы в этой строке. Чтобы вывести пользовательский символ на экран, используйте функцию write () с номером символа в качестве параметра. Параметры: ? led — переменная типа LiquidCrystal; ? num — номер создаваемого символа @ to 7); ? data — данные символьных пикселов. 10.2. Подключение дисплеев на контроллере HD44780 по протоколу 12С К сожалению, стандартная схема подключения ЖК-дисплеев на контроллере HD44780 не всегда удобна, т. к. занимает как минимум 6 цифровых выходов платы Arduino. Использование конвертера 12С (рис. 10.4) позволяет задействовать для подключения такого дисплея к плате Arduino только два вывода. Рис. 10.4. Подключение конвертера I2C к ЖК-дисплею Winstar Монтажная схема подключения ЖК-дисплея с конвертером 12С к плате Arduino представлена на рис. 10.5. Для определения 12С-адреса дисплея загрузим в плату Arduino скетч из листинга 9.5 и запустим монитор последовательного порта (рис. 10.6). Чтобы работать с ЖК-дисплеем WH1602 по протоколу 12С, необходимо установить библиотеку LiquidCrystal_I2C, которую можно скачать из репозитория Arduino, Для этого в меню Arduino IDE выбираем команду Эскиз | Include library | Manage Libraries. Далее в строке поиска набираем i2c led, находим библиотеку LiquidCrystal_I2C и нажимаем на кнопку Install (рис. 10.7).
182 Часть III. Практическое применение Ardumo +5В Рис 1Л5. Монтажная схема подключения ЖК-дисплея с конвертером!^ к плате Anluino Рис 10.6. Поиск 12С-устроиств, подключенных к АгаЧлпо
Глава 10. Использование дисплеев в проектах Arduino 183 Рис 10.7. Поиск библиотек UquidCfystal_l2C 10.2.1. Вывод на ЖК-дисплей данных с датчика, работающего по протоколу I2C Рассмотрим пример вывода на ЖК-дисплей данных с датчика атмосферного давления и температуры ВМР280, работающего по протоколу 12С. Этот датчик может измерять атмосферное давление и температуру с очень высокой точностью. Так, точность измерения барометрического давления этим датчиком составляет ±1 гПа, а температуры— ±1,0°С. Поскольку давление изменяется с высотой, датчик ВМР280 также можно использовать и как высотомер с точностыо±1 м. Обратите внимание, что в представленной на рис. 10.8 монтажной схеме этого проекта питание датчика ВМР280 берется с выхода Arduino 33 В. Подключение его к напряжению 5 В приведет к выходу датчика из строя!!! Дня работы датчика ВМР280 с Arduino необходимо установить библиотеку ВМР280, которую можно скачать со страницы: https://gitbeb.coiii/adafoHt/Adafriiit ВМР280 Library Электронный архив Библиотека Adafruit_BMP280JJbfary размещена в каталоге fbraries сопровождающего кнму электронного архива (см. приложение 2). С помощью созданного для этого проекта скетча (листинг 10.1) мы будем получать с датчика данные с периодичностью 5 с и выводить их как в фиксированные позиции ЖК-дисплея, так и в монитор последовательного порта.
184 Часть III. Практическое применение Arduino Рис. 10.8. Монтажная схема подключения к плате Arduino ЖК-дисплея с конвертером I2C и датчика ВМР280 // подключение библиотеки Wire #include "Wire.h" #include <SPI.h> // подключение библиотеки LiquidCrystal_I2C #include <LiquidCrystal_I2C.h> // создание экземпляра объекта для дисплея 16x2 LiquidCrystal_JE2C lcd@x27, 16, 2) ; // подключение библиотек для датчика ВМР280 #include <Adafruit_Sensor.h> #include <Adafruit_BMP280.h> ¦// создание экземпляра объекта - подключение по I2C Adafruit_BMP280 myBMP280; // для периода опроса датчика unsigned long millisl=0; int val; void setup() { // запуск последовательного порта на скорости 9600 бод Serial.begin(9600); // запуск ВМР280 myBMP280.begin();
Глава 10. Использование дисплеев в проектах Arduino 185 II инициализация дисплея lcd.initO ; // включить подсветку led.backlight(); // led.setCursor@,0); lcd.print("P="); led.setCursor(8,0); lcd.print("T="); void loop () { // раз в 5 с if (millis()-millisl>=5000) { // данные атмосферного давления с ВМР280 int val=(int)myBMP280.readPressure(); Serial.print("Pressure: ") ; Serial.print(val); Serial, printing Pa"); led.setCursorB,0); led.print(val); led.print("Pa"); // данные температуры с ВМР280 int val=(int)myBMP28 0.readTemperature(); Serial.print("Temperature: "); Serial.print(val); Serial.printIn(" *C"); led.setCursorA0,0); led.print(val); led.print("*C"); // начать отсчет 5 с заново millisl=millis (); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_01 сопровождающего книгу электронного архива (см. приложение 2). При компиляции скетча может произойти ошибка из-за отсутствия библиотеки Adafruit Unified Sensor. В этом случае необходимо доустановить эту библиотеку. Ее можно найти в репозитории Arduino. Для этого в Arduino IDE выбираем пункт Эскиз | Include library | Manage Libraries, находим библиотеку Adafruit Unified Sensor и нажимаем на кнопку Install.
186 Часть III. Практическое применение Arduino 10.3. Графический дисплей Nokia Символьные дисплеи, которые мы рассматривали в предыдущих разделах, имеют большие возможности в плане вывода символьной информации. С их помощью можно выводить текстовые сообщения, значения различных параметров, показания датчиков. Но что если нам требуется еще больший уровень информативности — с отображением графиков, картинок или текстовых сообщений с использованием разных шрифтов? Поможет нам в этом деле графический дисплей— например, Nokia5110. Этот дисплей широко распространен среди любителей Arduino. Дисплей монохромный и имеет разрешение 84x48 точек. Как правило, дисплеи NokiaSl 10 поставляются на плате в паре с контроллером PCD8544 и штыревым разъемом. В продаже можно найти два варианта исполнения модулей дисплея: на синей плате (рис. 10.9, слева) и на красной (рис. 10.9, справа). Оба варианта модуля имеют по восемь выводов: П RST —сброс (Reset); ? СЕ — выбор устройства (Chip Select); ? DC — выбор режима (Data/Command select); ? Din — данные (Data In); ? SCLK — тактирующий сигнал (Clock); П VCC — питание 33 B; О BL (LIGHT) — подсветка; П GND — «земля». Щ:Г0ШШЩШшШш \ Рис. 10.9. Дисплеи Nokia5110: слева—на синем плате; справа—на фасной Питание контроллера PCD8S44 должно лежать в пределах 2,7-3,3 В (максимум 33 В — при подаче S В дисплей может выйти из строя). Сигнальные же выводы толерантны к напряжению 5 В и подключаются к любым цифровым выводам Arduino.
Глава 10. Использование дисплеев в проектах Ardumo 187 При использовании синего модуля подсветка дисплея активизируется подачей сигнала высокого уровня на вывод BL, а при использовании красного — подачей сигнала низкого уровня на вывод LIGHT. Монтажная схема подключения дисплея Nokia5110 к плате Arduino показана на рис. 10.10. Рис 10.10. Монтажная схема подключения дисплея Nakia5110 к плате Апкнпо Для управления дисплеем Nokia5110 потребуется библиотека Adaftnit GFX Library, которую можно скачать по адресу: fcttps^/gMiebxora/adbifnMt/Adafnut- GFX-Library/arcluvc/nMsterjdp. Установите эту библиотеку и включите в скетч следующие строки: tinclode <Ad&fruit_GFX.h> finclude <Adafruit_PCD8544.h> // Nokia 5110 // pin 1 - Serial clock out (SCUt) // pin 6 - Serial data out (DIN) // pin 5 - Data/Ccraoand select (D/C) // pin 4 - LCD chip select (CS) // pin 3 - UCD reset (RST) Adafruit_PCD8544 display = Mafruit_PCD8544 G, 6, 5, 4, 3); Электронный архив Библиотека Adafln?jGFX_Ubrary размещена в каталоге Sbrahes сопровождающего книгу электронного архива (см. приложение 2).
188 Часть III. Практическое применение Arduino В листинге 10.2 показан простой скетч вывода текста на экран дисплея Nokia5110. #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> // Nokia 5110 // pin 7 - Serial clock out (SCLK) // pin 6 - Serial data out (DIN) // pin 5 - Data/Coiranand select (D/C) // pin 4 - LCD chip select (CS) // pin 3 - LCD reset (RST) Adafruit_PCD8544 display = Adafruit_PCD8544G, 6, 5, 4, 3); void setup() { // инициализация и очистка дисплея display.begin(); display.clearDisplay(); display.display(); display.setContrastE0); // установка контраста delayA000); display.setTextSizeA); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.setCursor@,0); // установка позиции курсора display.println("Hello, world!"); display.display(); void loop() { Загрузите этот скетч в плату Arduino, и вы увидите вывод на экран текста Hello, world! Теперь попробуем поработать с графикой — выведем на экран простейшие геометрические фигуры (листинг 10.3). #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> // Nokia 5110 // pin 7 - Serial clock out (SCLK) // pin 6 - Serial data out (DIN) // pin 5 - Data/Command select (D/C)
Глава 10. Использование дисплеев в проектах Arduino // pin 4 - LCD chip select (CS) // pin 3 - LCD reset (RST) Adafruit_PCD8544 display = Adafruit_PCD8544 G, 6, 5, 4, 3); void setup () { // инициализация и очистка дисплея display.begin(); display.clearDisplay(); display.display(); display.setContrastE0); // установка контраста delayA000); void loop () { // пиксел display.clearDisplay(); display.drawPixelA0, 10f BLACK); display.display(); delayA000); // линия display.clearDisplay(); display.drawLine@, 0, 50, 30, BLACK); display.display(); delayA000); // прямоугольник display.clearDisplay(); display.drawRect@, 0, 10, 10, BLACK); display.display(); delayA000); // прямоугольник залитый display.clearDisplay(); display.fillRect@, 0, 10, 10, BLACK); display.display(); delayA000); // треугольник display.clearDisplay(); display.drawTriangle@, 0, 40, 40, 30, 20, BLACK); display.display(); delayA000); // окружность в центре display.clearDisplay(); display.drawCircle(display.width()/2, display.heigtit()/2, 10, BLACK); display.display(); delayA000);
190 Часть Ш. Цракипуюшю щшш&ювжю Antoho Электронный архив Полный вариант рассмотренного светча наяурпся в лапше ехатрЛеяМО(_10_аз сопрово- яфцэкхцого янму элбкпюнного арнива Сем. лрютиввмив 2V. Библиотека Adafimk GFX Libf«y позволяет выводил» на экран дисплея и растровые июбражениж. Для начала веобходщю создать саму мциинку— в черно-белом формате с расширением bmp и размером 84x8 пикселов. А затем получить НЕХ-код этой щциинки— например, в программе GLCD Took— и вставить ого в скетч Aiduino (листинг 10.4). Загрузите этот скетч в плату АпЬшю, и вы уввдите на экране растровое изображение (рис. 10.11). Рве 10.11. Двупрстиая i # include <MafruitjG?X.n> #indiade <Mafr3iit_BCDB544.n // Nokia 5110 // pin 1 - Serial clock out // pin 6 - Serial data ©ait ЩШ) // pie 5 - Data/OaramaiMi select // pin 4 - LCD chip select fCS) // pin 3 - ILCB reset (K$T$ i^dafoiit_roD©S44 display = Adafruit_PCDB544 f 1, 6. 5, 4, 3); canst unsigned char ЖХЭЖМ imglOO = f Oxff, Qxff, Oxffr ®x?f0 0xff# !Qxff# 0xffr Qxff# Oxff, Qxff, 0x80, Oxff# Qxff, Oxff# 0xe©, Qxff# 0xfc# 0xlf# Qxff# Qxff, Qxff, 0x80# Oxff, Oxff# Qxff, 0xe0# 0xffr ©xfc, Qxlf, Oxffr Qxff# Qxff, 0x80, Oxff, Oxff, Qxf?. 0xe0# ©xff, 0xfcr Oxlf, Oxff, Oxffr Qxff, Qx80# Oxff, Qxff, QxffF Qxe©, Qxff# 0xfc# (Dxlf, 0xffr Qxff, Qxff, Qx80# Qxffr Dxff, Qxffr ©xel# ©xff, Oxfc, Qxlf, Qxlf, Oxff# Qxff, 0x80, Qxff, Oxff, Oxff# 0xel# Oxff, ©xf®, ©xlf# Oxff, Oxff# Qxff# 0x80, Oxff, Oxff, Oxff, Oxcl, Oxff, ©xf8, Qx3f, Qxff, Oxff, Qxff, 0x80, Oxff, Oxff, Qxff, Oxcl, ©xff, ©xfB, Qx3f, Oxff, Oxff, Qxff, 0x80, Oxff, Oxff, Oxff, Oxcl, Oxff, OxfB, 0x3f, Oxff, Qxff, Qxff, 0x80, Oxff, Oxff, Oxff, Oxcl, Oxff, QxfB, Qx3f, Qxff, Qxff, Qxff, 0x80, Gxff, Oxff, Oxff, ОхсЗ, Qxff, QxfO, Qx3f, Qxff, Qxff, Qxff, 0x80, Oxff, Oxff, Oxff, 0x83, 0x3, QxfO, 0x20, 0x78, Qx3f, 0x83, 0x80, Oxff, Oxff, Oxff, 0x80,-0x1, OxfO, 0x40, 0x38, Qx3f, 0x83, 0x80, Oxff, Oxff, Oxff, 0x80, 0x0, QxfO, QxQ, 0x38, Qxlf, 0x7, 0x80, Oxff, Oxff, Oxff, 0x80, 0x0, OxfO, QxQ, 0x18, Qxlf, 0x7, 0x80, Oxff, Qxff, Oxff, 0x80, 0x0, QxfO, 0x10, Qxlc, Qxle, Qxf, 0x80, Dxff, Oxff, 0x3, 0x81, OxcO, 0x60, 0x78, Qxlc, Qxlc, Qxlf, 0x80, Oxff, Oxfc, 0x3, 0x3, OxeO, 0x60, 0x78, Qx3c, Qxlc, Qxlf, 0x80,
Гпава 10. Испапьэовамюдшятвеве проектах АйФжю 191 Oxff# OxfQ, 0x3, 0x7, ОжеО, 0x60, Qxf8, ОхЗс, 0x18, ©x3f, 0x80, Cxit, ОхсО, 0x3, 0x7, ОхеО, QxeO, Qxf8, ОхЗе, 0x8, 0x3f, 0x60, Oxff, 0x60, 0x3, 0x7, OxeO, QxeO, Qxf8, ОхЗе, QxQ, Qx7f, 19x80, Oxff, 0x0, 0x3, 0x7, ©xc©, OxeO, 0xf8, ОхЗе, 0x0, Oxff, 0x80, Oxfe, 0x0, 0x3, 0x7, QxcO, Qxd, Qxf8, ОхЗе, 0x0, ©xff, 0x80, Oxfe, 0x0,0x6, 0x3, Oxl, Oxd, OxfO, 0x7e, Oxl, Qxff, 0x80, Oxfe, 0x0, 0x6, QxO, Oxl, Oxcl, OxfO, 0x7f, Oxl, Oxff, 0x80, OxfS, QxO, 0x6, 0x0, 0x3, Oxcl, OxfO, 0x7f, 0x3, Oxff, 0x80, OxfO, 0x0, Oxle, 0x0, 0x7, Oxcl, OxfO, 0x7f, 0x7, Oxff, 0x80, OxfO, 0x0, 0x7e, 0x10, Oxf, 0x81, OxfO, 0x7f, 0x7, Oxff, 0x80, OxeO, Oxl, Oxfe, 0x18, Oxlf, ОхсЗ, OxfO, Oxff, 0x8f, Oxff, 0x80, OxeO, Oxl, Oxff, Oxff, Qxff, Oxff, ©xff, Qxff, Oxff, Oxff, 0x80, OxeO, 0x3, Oxfe, 0x0, 0x0, 0x0, 0x0, 0x0, ©xlf, Oxff, 0x80, OxeO, 0x3, Oxfe, 0x0, 0x0, 0x0, 0x0, 0x0, Oxlf, Oxff, 0x80, OxcO, 0x7, Oxfe, 0x0, QxO, QxQ, OxQ, 0x0, 0x3f, Qxff, 0x80, OxcO, 0x7, Oxff, Oxff, Qxff, Oxff, Oxff, Oxff, Qxff, Oxff, 0x80, OxcO, 0x7, Oxff, Oxfe, QxO, 0x7f, Qxff, Qxff, Qxff, Oxff, 0x80, OxcO, 0x7, Oxff, Oxfe, 0x0, ©xff, Oxff, Oxff, Qxff, Oxff, 0x80, OxcO, 0x7, Oxff, ©xfB, 0x0, Oxff, Oxff, Oxff, Oxff, Qxff, 0x80, OxeO, 0x3, Oxff, 0xf8, 0x0, Oxff, Oxff, Qxff, Oxff, Oxff, 0x80, OxeO, 0x3, Oxff, 0xf8, 0x0, Oxff, Oxff, Oxff, Oxff, Oxff, 0x80, OxeO, Qxl, Oxff, OxfO, OxQ, Qxff, Qxff, Qxff, Oxff, Oxff, 0x80, OxeO, 0x0, Oxff, OxeO, Qxl, Qxff, Qxff, Oxff, Dxff, Oxff, 0x80, OxfO, 0x0, 0x7f, 0x80, Oxl, Oxff, Qxff, Oxff, Oxff, Oxff, 0x80, OxfO, 0x0, 0x4, 0x0, 0x3, Oxff, Oxff, Oxff, Oxff, Oxff, 0x80, OxfS, 0x0, Ox©, 0x0, 0x3, Oxff, Oxff, Oxff, Oxff, Oxff, 0x80, Oxfe, 0x0, 0x0, 0x0, 0x7, Oxff, Oxff, Oxff, Oxff, Oxff, 0x80, Oxfe, QxO, 0x0, 0x0, 0x7, Qxff, Oxff, Oxff, ©xff, Oxff, 0x80, Oxfe, 0x0, 0x0, 0x0, Oxf, Oxff, Oxff, Oxff, Qxff, Oxff, 0x80 void setup О { // инициализадия и ичжтка дасплея display. begin О ; display - clearBisplay О ; display, display О ; // установка контраста display. setCantrast: {50}; delay A000); // отрисовка изображения display.drawBitJaapiO, 0, iiaglO, 84, 44, ВШЖ); display.display О ; void loop<0 {
192 Часть III. Практическое применение Arduino Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_04 сопровождающего книгу электронного архива (см. приложение 2). 10.4. OLED-дисплеи В последнее время в проектах Arduino все чаще используют не простые ЖК-инди- каторы, а OLED-дисплеи, несмотря на то, что они дороже. Но, в отличие от ЖК- индикаторов, где пикселы подсвечиваются, в OLED-дисплеях они сами излучают свет— изображение получается более контрастным и насыщенным, с хорошими углами обзора. К тому же OLED-дисплеи обладают незначительным энергопотреблением. Для использования в Arduino-проектах удобно применять OLED-дисплеи, выполненные в виде модуля на микросхеме SSD1306 с необходимой обвязкой. Эти модули работают на интерфейсе 12С. Монтажная схема подключения модуля OLED- дисплея к плате Arduino показана на рис. 10.12. Рис. 10.12. Монтажная схема подключения к плате Arduino OLED-дисплея 128x32 Сначала определим адрес нашего дисплея на шине 12С. Для этого загрузим в плату Arduino скетч из листинга 9.5 и запустим монитор последовательного порта. Результат работы скетча показан на рис. 10.13— найдено устройство с адресом ОхЗС. Для работы с OLED-дисплеем 128x32 имеется несколько библиотек. Самая известная— Adafruit SSD1306. Есть также и библиотека OLEDI2C, которая благодаря
Глава 10. Использование дисплеев в проектах Arduino усилиям энтузиастов поддерживает использование русского шрифта. Для подключение русского шрифта включите в скетч команду: extern uint8_t RusFont[]; Затем в нужном месте скетча выберите шрифт: display. setFont (RusFont) ; К сожалению, в скетче Arduino мы не можем набирать слова русскими буквами, поэтому вводим нужный текст в английской раскладке: // текст "Издательство БХВ" display.print ("Bplfntkmcndj <{D", CENTER, 40); Рис. 10.13. Адрес OLED-дисплея 128x32 найден Электронный архив Библиотека OLEDJ2C размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). 10.4.1. Электронные часы на OLED-дисплее В этом примере мы воспользуемся OLED-дисплеем, чтобы создать макет электронных часов. Датчиком точного времени нам послужит модуль DS3231 на микросхеме DS3231 (рис. 10.14)— он представляет собой недорогую плату с чрезвычайно точными часами реального времени (RTC), с календарем, дополнительной памятью NW SRAM E6 байтов) и возможностью температурной компенсации кварцевого генератора и кристалла. Количество дней в месяце рассчитывается с учетом високосных лет до 2100 г.
194 Часть III. Практическое применение Arduino Рис. 10.14. Модуль датчика точного времени DS3231 Модуль несет на себе литиевую батарею, которая поддерживает бесперебойную работу даже при отключении источника питания. Микросхема подключается к плате Arduino при помощи шины 12С. Монтажная схема подключения к плате Arduino модуля датчика точного времени и OLED-дисплея показана на рис. 10.15. Рис. 10.15. Монтажная схема подключения к плате Arduino модуля датчика точного времени и OLED-дисплея
Глава 10. Использование дисплеев в проектах Arduino 795 В скетче для этого проекта (листинг 10.5) мы получаем время с микросхемы DS3231, форматируем его и выводим на дисплей (рис. 10.16). При этом используются два вида шрифтов: 0 smaiiFont — для вывода времени и даты; 0 RusFont — для вывода дня недели. Рис. 10.16. Макет электронных часов в сборе // подключение библиотек finclude <Wire.h> tinclude <Time.h> iinclude <DS1307RTC.h> iinclude <OLED_I2C.h> OLED display(SDA, SCL, 8); // extern uint8_t SmallFontf]; // шрифт small extern uint8_t RusFont []; // шрифт RusFont tmElements_t tm; String strl=IMI; // дни недели char wday[7] [12] = { {"Gjytltkmybr"}, {" Dnjhybe "},{" Chtlf "}, {" Xtndthu "},{" Gznybwf "},
196 Часть III. Практическое применение Artiu'm {" Ce,,jnf "},{"Djcrhtctymt"} void setup() { display.begin(); void loop() { if (RTC.read(tm)) { // Стираем все с экрана display.clrScr(); // Выбираем шрифт' display.setFont(SmallFont); // строка время strl=""; strl=strl+ printf2 (tm.Hour); strl=strl+":"; strl=strl+ printf2 (tm.Minute); strl=strl+":ff; strl=strl+ printf2 (tm.Second); display.print(strl, CENTER, 10); // строка дата strl=""; strl=strl+ printf2 (tm.Day); strl=strl+"/"; strl=strl+ printf2 (tm.Month); strl=strl+'7"; strl=strl+String(tmYearToCalendar(tm.Year)); display.print(strl, CENTER, 22); display.setFont(RusFont); // строка день недели display.print(wday[tm.Wday], CENTER, 34); // Обновляем информацию на дисплее display.update(); delayA000); // вывод с добавлением до двух цифр String printf2(int nn) { String snn=""; if (nn >= 0 && nn < 10) {snn=";} snn=str+String(nn); return snn; }
Глава 10. Использование дисплеев в проектах Arduino 197_ Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_05 сопровождающего книгу электронного архива (см. приложение 2). 10.5. Дисплеи Nextion К этому моменту мы уже рассмотрели подключение к Arduino различных дисплеев: символьных, графических монохромных Nokia5110, а также современных OLED- дисплеев. Однако чем сложнее дисплей, тем больше вычислительных ресурсов платы Arduino он использует. В этом плане весьма интересен дисплей Nextion, который представляет собой полноценный компьютер с процессором, видеокартой и экраном, причем он выделяет весь свой вычислительный ресурс на обработку графики и позволяет записывать в него свои программы. На модулях дисплеев Nextion имеется разъем UART и выводы GPIO, что дает возможность использовать дисплеи Nextion как совместно с Arduino — подключая дисплей к Arduino по шине UART (обмен с помощью унифицированных команд), так и отдельно— подключая кнопки, светодиоды, реле и пр. напрямую к выводам GPIO дисплея). Через имеющийся у дисплея Nextion разъем для карт памяти micro-SD в него можно загружать программы. Для работы с дисплеями Nextion необходимо установить программу Nextion Editor, которая позволяет создавать интерфейс пользователя с использованием различных библиотечных элементов: кнопок, слайдеров, картинок, графики, текста и т. п., а также прописывать алгоритмы поведения дисплея для различных событий элементов, формирующих этот интерфейс. Скачать программу Nextion Editor можно со страницы загрузки официального сайта: https://nextion.itead.cc/resources/download/. Версия там имеется только для операционной системы Windows. Итак, скачиваем, распаковываем, устанавливаем и запускаем программу Nextion Editor. Перед нами откроется графическое окно разработки (рис. 10.17): 1. Главное меню. 2. Меню управления выравниванием и порядком элементов. 3. Библиотека элементов. 4. Область отображения. 5. Список страниц проекта. 6. Библиотека изображений/Библиотека шрифтов. 7. Окно вывода результатов компиляции. 8. Окно для ввода кода, выполняемого при возникновении события. 9. Зона редактирования атрибутов выбранного элемента.
198 Часть III. Практическое применение Arduino Рис. 10.17. Элементы окна программы Nextion Editor 10.5.1. Создание нового проекта для дисплея Nextion Командой меню File | New создадим новый проект, введем название будущего проекта и нажмем на кнопку Сохранить — откроется окно Setting с тремя вкладками: Device, DISPLAY и project: ? на вкладке Device выберем линейку и модель дисплея (рис. 10.18); ? на вкладке DISPLAY — ориентацию дисплея и кодировку (рис. 10.19). Для поддержки кириллицы необходимо выбрать кодировку iso-8859-5; ? на вкладке project можно установить пароль доступа к проекту. Приступим теперь к добавлению элементов. Изначально в окне списка страниц присутствует одна страница — page, название которой можно изменить. У каждого элемента, а также у страницы, имеются свойства (атрибуты), которые также можно изменять в окне редактирования атрибутов выбранного элемента. Установим для страницы фон, предварительно добавив изображение в области 6 окна программы (библиотека изображений), нажав на кнопку +. Теперь можно определить нужный фон, выбрав для страницы page атрибут sta, равный image, и нужное изображение из списка в поле pic (рис. 10.20). Затем добавим шрифты, которые наверняка пригодятся для надписей на экране. Шрифты можно сгенерировать генератором шрифтов. Выполняем команду меню Tools | Font Generator и в открывшемся окне выбираем параметры шрифта (рис. 10.21), после чего сохраняем сделанный выбор, нажав на кнопку Generare font.
Fnaea 10. Использование дисплеев в проектах Arduino 199 Рис. 10.18. Выбор модели дисплея Рис. 10.19. Выбор ориентации дисплея и кодировки
200 Часть III. Практическое применение Arduino Рис. 10.20. Добавление фона для страницы Рис. 10.21. Генерация шрифта
Гпава 10. Использование дисплеев в проектах Arduino 201 Рис. 10.22. Шрифты проекта Сгенерированный шрифт можно добавить в проект. Для этого в библиотеке шрифтов нажимаем на кнопку + и указываем путь к шрифту. Теперь добавленный шрифт можно использовать в проекте (рис. 10.22). Электронный архив Иллюстрации и шрифты, использованные в этом проекте, находятся в папке \examples\ 10\пехИоп_10__1 сопровождающего книгу электронного архива (см. приложение 2). Элементы интерфейса добавляются на экран из библиотеки элементов, местоположение элемента на экране устанавливаем, перемещая его с помощью мыши, для активного элемента редактируем его атрибуты (рис. 10.23). Б I - л,- Рис. 10.23. Редактирование атрибутов элемента
202 Часть III. Практическое применение Arduino Установив соответствующий флажок, задаем отправку команд выбранного события для UART (Touch Press или Touch Release), в поле кода можно добавить код для этого события (рис. 10.24). Рис. 10.24. События для элемента Перед прошивкой модуля необходимо проект скомпилировать, выполнив команду меню Compile. Если ошибок нет, можно приступать к прошивке модуля. Дисплеи Nextion поддерживают два вида прошивки: ? через последовательный порт UART; ? с помощью карты microSD. 10.5.2. Прошивка дисплея через UART Для прошивки дисплея через UART понадобится адаптер USB-Serial. Схема подключения Nextion к адаптеру USB-Serial показана на рис. 10.25. Для загрузки прошивки выбираем пункт меню Upload и в открывшемся окне нажимаем на кнопку Go. Процесс прошивки будет отображаться в окошке программы (рис. 10.26) и на дисплейном модуле. По окончании прошивки загружаемый проект начнет выполняться и отображаться на дисплейном модуле. Надо отметить, что прошивка через UART занимает весьма долгое время.
Глава 10. Использование дисплеев в проектах Arduino 203 ^4iM; ' Рис. 10.25. Схема подключения дисплея Nextion к адаптеру USB-Serial Рис. 10.26. Загрузка прошивки в дисплей Nextion через последовательный порт 10.5.3. Прошивка дисплея с помощью карты microSD Подключите к компьютеру карту microSD и скопируйте на нее файл с именем проекта. Извлеките карту из компьютера и вставьте ее в соответствующий разъем дисплея. Подключите на дисплей питание и дождитесь окончания прошивки. Извлеките карту. После выполнения всех этих действий загружаемый проект начнет выполняться и отображаться на дисплейном модуле. 10.5.4. Подключение дисплея Nextion к плате Arduino Рассмотрим подключение дисплея Nextion к плате Arduino и организацию их двустороннего взаимодействия. Взаимодействие это происходит по последовательному порту. Монтажная схема подключения дисплея Nextion к плате Arduino показана на рис. 10.27. Для взаимодействия с дисплеем Nextion мы воспользуемся Arduino-библиотекой Nextion. При этом со стороны Arduino задействуем программный последовательный порт Software на пинах 2 (RX) и 3 (ТХ).
204 Часть III. Практическое применение Arduino Рис. 10.27. Монтажная схема подключения дисплея Nextion к плате Arduino Электронный архив Библиотека Nextion размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Содержимое скетча получения данных из дисплея Nextion при событиях Touch Press и Touch Release созданной ранее прошивки для дисплея Nextion представлено в листинге 10.6. // подключение библиотеки для работы с Software Serial #include <SoftwareSerial.h> // подключение библиотеки для работы с Nextion #include <Nextion.h> // Nextion TX к пин 2, RX к пин 3 Arduino SoftwareSerial nextionB, 3); // создание объекта Nextion к порту на скорости 9600 бод Nextion myNextion(nextion, 9600); void setup() { // запуск последовательного порта Serial.begin(9600); // инициализация Nextion myNextion.init();
Глава 10. Использование дисплеев в проектах Arduino 205 void loop () { // ожидание сообщения от Nextion String message = myNextion.listen(); if (message != ""H // при получении сообщения - // вывести его в последовательный порт Serial.println(message); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_06 сопровождающего книгу электронного архива (см. приложение 2). Загружаем этот скетч в плату Arduino и видим в последовательном порту подключение данных из Nextion при назначенных для элементов событиях Touch Press и Touch Release (рис. 10.28). Рис. 10.28. Получение данных от дисплея Nextion по последовательному порту Разберем формат полученной строки: 65 0 2 1 ffff fffff fffff Здесь: ? 65 — первый байт: произошло событие TouchEvent; ? о — номер страницы;
206 Часть III. Практическое применение Arduino ? 2 — номер элемента на странице; ? 1 — событие Touch Press @ — Touch Release). Теперь рассмотрим отправку команд на дисплей Nextion (выбор страницы, установка атрибутов элементов, переключение кнопок, получение атрибутов элементов и пр.). В библиотеке Nextion имеются методы для отправки команд на дисплей Nextion — например: ? sendcommand () — отправить команду; ? setComponentText () — установить текст компонента; ? buttononof f () — нажать/отжать кнопку; ? buttonToggie () — переключение переключателя; ? updateProgressBar () — изменить компонент ProgressBar; ? getcomponentText () — получить текст компонента. Содержимое скетча отправки команд на дисплей Nextion представлено в листинге 10.7. // подключение библиотеки для работы с Software Serial #include <SoftwareSerial.h> // подключение библиотеки для работы с Nextion #include <Nextion.h> // Nextion TX к пин 2, RX к пин 3 Arduino SoftwareSerial nextionBf 3); // создание объекта Nextion к порту на скорости 9600 бод Nextion myNextion(nextion, 9600); // служебные переменные char buf[10]; void setup() { // запуск последовательного порта Serial.begin(9600); // инициализация Nextion myNextion.init(); void loop() { // изменение текста на компоненте t0 myNextion.setComponentText("tO", "Smartf4String(millis())); delayB000); // изменение размера кнопки ЬО myNextion.sendCommand( "bO.font=l");
Глава 10. Использование дисплеев в проектах Arduino 207 delayB000); myNextion.sendCommand( "ЬО.font=0"); delayB000); // изменение позиции слайдера String s="hO.val="+String(random@,100)); Serial.println(s); s.toCharArray(buf, 10); myNextion.sendCommand(buf); delayB000); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_07 сопровождающего книгу электронного архива (см. приложение 2). Загружаем скетч на плату Arduino и видим на дисплее изменение атрибутов компонентов. 10.6. Светодиодные матрицы Познакомимся с еще одним типом устройств, которые можно использовать для визуализации проектов Arduino. Если скомпоновать светодиоды не в виде цифры или шкалы, а в виде сетки, то получится графический индикатор, на котором можно отобразить не только число, но и какое-либо изображение. Такая сетка называется матричным индикатором, а в случае использования светодиодов — светодиодной матрицей. 10.6.1. Четырехразрядная светодиодная матрица Разрешение матричного индикатора — это количество точек по горизонтали и вертикали. Самые распространенные индикаторы имеют разрешение 8x8 точек. Схема соединений светодиодов в матрице показана на рис. 10.29. Для создания изображения на такой матрице с использованием динамической индикации необходимо задействовать 16 контактов Arduino! К счастью, разработаны специализированные микросхемы для управления индикаторами— например, МАХ721, которая позволяет управлять матрицей по трем проводам. Если требуется светодиодная матрица с большим разрешением, то ее составляют из нескольких индикаторов 8><8. Монтажная схема подключения четырехразрядной светодиодной матрицы 4x8x8 показана на рис. 10.36. Для ее питания необходимо использовать внешний источник 5 В. При программировании матрицы мы подключим библиотеку Max72xxPanel. Дополнительно надо установить и библиотеку Adafruit-GFX-Library, необходимую для вывода на дисплей графических примитивов.
208 Часть III. Практическое применение Arduino Rl R2 R3 R4 R5 R6 R7 R8 а С1 С2 СЗ С4 С5 С6 С7 С8 Рис. 10.29. Схема соединения светодиодов в матрице 8x8 Рис. 10.30. Монтажная схема подключения матрицы 4x8x8 к плате Arduino Электронный архив Библиотеки Max72xxPanel и Adafruit-GFX-Library размещены в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
Глава 10. Использование дисплеев в проектах Anduino 209 В листинге 10.8 представлен скетч для вывода точки и гашения точки на матрицу в произвольные позиции: ? получаем произвольные позиции координат и зажигаем светодиоды: matrix.drawPixei (x, у, HIGH); ? или гасим светодиод в матрице: matrix.drawPixei(x, у, LOW); О после включения и выключения пикселов с помощью функции drawPixei () необходимо вызвать функцию write (). // подключение библиотек tinclude <SPI.h> iinclude <Adafruit__GFX.h> iinclude <Max72xxPanel.h> // пин CS int pinCS = 10; // количество матриц по горизонтали int numberOfHorizontal = 4; // количество матриц по вертикали int numberOfVertical = 1; // создание объекта Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontal, numberOfVertical); // координаты х,у int x, y; void setup () { // яркость от 0 до 15 matrix.setlntensity(8); void loopO { x=random@,32); y=random@,8); // зажигаем пиксел matrix.drawPixei(x, y, HIGH); // вывод всех пикселов на матрицы matrix.write(); у delayA0); x=random@,32); y=random@,8); // гасим пиксел matrix.drawPixei(xf y, LOW);
210 Часть III. Практическое применение Ardumo // вывод всех пикселов на матрицы matrix.write(); Загружаем скетч в плату и наблюдаем за меняющимся заполнением экрана. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\W\_10_08 сопровождающего книгу электронного архива (см. приложение 2). 10.6.2. Вывод на четырехразрядную светодиодную матрицу спрайтов и символов Попробуем вывести на экран маленькую картинку — спрайт. Для создания изображения спрайта воспользуемся массивом из восьми байтов. Каждый байт массива будет отвечать за строку матрицы, а каждый бит в байте — за точку в строке: const byte spritel[8] = { 0В00111100, 0B01000010, 0B10100101, 0B10000001, 0B10100101, 0B10011001, 0B01000010, 0B00111100 }; Осталось загрузить в плату Arduino скетч из листинга 10.9 (попиксельный вывод картинки из массива). // подключение библиотек #include <SPI.h> #include <Adafruit_GFX.h> #include <Max72xxPanel.h> // пин CS int pinCS = 10; // количество матриц по горизонтали int numberOfHorizontal = 4; // количество матриц по вертикали int numberOfVertical = 1; // создание объекта Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontal, numberOfVertical); // изображение const byte spritel[8] = { ObOOllllOO, ObOlOOOOlO,
Глава 10. Использование дисплеев в проектах Arduino 211 0Ы0100101, 0Ы0000001, 0Ы0100101, 0Ы0011001, оьоюооою, ObOOllllOO void setup () { // яркость от 0 до 15 matrix.setIntensityF); // очистка экрана matrix.fillScreen(LOW); for ( int у = 0; у < 8; y++ ) { for ( int x = 0; x < 8; x++ ) { // зажигаем х-й пиксел в у-й строке matrix.drawPixel (х, у, spritel[y] & A«х)); // вывод всех пикселов на матрицу matrix.write(); void loop() {; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\1C\_10_09 сопровождающего книгу электронного архива (см. приложение 2). После загрузки скетча на экране видим изображение, но оно повернуто на 90 градусов! А причина в том, что одноразрядные матрицы в нашей сборке стоят неправильно (рис. 10.31). Нас выручит имеющаяся в библиотеке Max72xxPanel функция setRotation (), которая задает ориентацию изображения на матрице: matrix.setRotation@,1); Здесь первый параметр — это индекс матрицы, а второй — количество поворотов на 90 градусов. На матрицу можно вывести любой символ — например, букву или цифру. Для этого необходимо для нужных символов создать соответствующие изображения. К счастью, в библиотеке Adafruit-GFX-Library помимо функций для работы с графикой и текстом имеется и база латинских букв в верхнем и нижнем регистрах, а также все знаки препинания и прочие служебные символы. Символы имеют размер 5x8. Отобразить символ на матрице можно с помощью функции drawchar (): drawChar( х, у, символ, цвет, фон, размер);
212 Часть III. Практическое применение Arduino Как должно быть @,0) Как в сборке Рис. 10.31. Схемы соединения матриц В листинге 10.10 приведен скетч вывода текста на нашу 4-разрядную светодиодную матрицу. // подключение библиотек #include <SPI.h> #include <Adafruit_GFX.h> #include <Max72xxPanel.h> // пин CS int pinCS =10; // количество матриц по горизонтали int nuinberOfHorizontal = 4; // количество матриц по вертикали int numberOfVertical = 1; // создание объекта Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontal, numberOfVertical); // текст для вывода String text = "Ok95!"; void setup() { // яркость от 0 до 15 matrix.setIntensityG); void loopO { // очистить экран matrix.fillScreen(LOW);
Глава 10. Использование дисплеев в проектах Arduino 213 for ( int i = 0 ; i < text.length(); i++ ) { // поворот на 90 градусов matrix.setRotation( i, 1 ); // вывод символов matrix.drawChar(i*6, 0, text[i], HIGH, LOW, 1); } // вывод на матрицу matrix.write(); delayA000); После загрузки скетча на экране видим текст! Электронный архив Полный вариант рассмотренного скетча находится в папке examples\iO\_1O__W сопровождающего книгу электронного архива (см. приложение 2). 10.6.3. Бегущая строка на четырехразрядной светодиодной матрице Создадим на 4-разрядной светодиодной матрице «бегущую строку». Схема соединений показана на рис. 10.32. Потенциометром мы воспользуемся для регулирования скорости «бега». I-5B Рис. 10.32. Монтажная схема соединения для «бегущей строки» на 4-разрядной светодиодной матрице
214 Часть III. Практическое применение Arduino Приступим к написанию скетча (листинг 10.11): ? строка для вывода будет храниться в переменной text: String text = "Arduino BHV "; ? размер матрицы по горизонтали — 32 позиции. Длина «бегущей строки» равна длине строки, умноженной на 6 E — ширина символа + 1 интервал между символами); ? переменная offset — координата начала строки. Начиная с этой координаты, выводим символы на матрицу: • крайнее левое положение: offset=0-text.length() * 6; • по достижении крайнего правого положения устанавливаем of f set=32; ? количество матриц по горизонтали берем с запасом, чтобы не происходило сбоев с выводом: int numberOfHorizontal = 15; ? скорость движения регулируем потенциометром. // подключение библиотек #include <SPI.h> #include <Adafruit_GFX.h> #include <Max72xxPanel.h> // пин CS int pinCS = 10; // количество матриц по горизонтали (берем с запасом! !!) int numberOfHorizontal = 15; // количество матриц по вертикали int numberOfVertical = 1; // создание объекта Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontal, numberOfVertical); // строка для вывода String text = "Arduino BHV"; // текущее смещение от 0 int offset=32; // максимальное значение скорости int maxspeedl^lOO; // минимальное значение скорости int minspeedl=1000; void setup() { // яркость от 0 до 15 matrix.setIntensityG);
Глава 10. Использование дисплеев в проектах Arduino 215 void loop () { // очистка экрана matrix.fillScreen(LOW); // вывод строки с позиции offset for ( int i = 0 ; i < text.length(); i++ ) { matrix.setRotation( i, 1 ); if(i*6+offset>(-6) && i*6+offset<32) { matrix.drawChar(i*6+offset, 0, text[i], HIGH, LOW, 1); matrix.write(); // задержка (скорость) int speedl=(analogRead(АО),0,1023,minspeedl,maxspeedl); delay(speedl); // изменение смещения offset=offset-1; //в начало - позиция 32 if(offset+text.length()*6==0) { offset=32; Загружаем скетч на плату Arduino и наблюдаем «бегущую строку», скорость ее «бега» регулируем потенциометром. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\W\_W_ii сопровождающего книгу электронного архива (см. приложение 2). 10.6.4. Русификация «бегущей строки» на четырехразрядной светодиодной матрице В разд. 10.6.3 мы создали на 4-разрядной светодиодной матрице «бегущую строку». Однако, если в эту строку вставить русские буквы, мы получим в ней крако- зябры. Это происходит из-за того, что в графической библиотеке Adafruit_GFX нет русского шрифта. Следовательно, его необходимо туда добавить. Для этого надо заменить в файле glcdfont.c из библиотеки Adafruit GFX определенные символы на русские в нужной кодировке. Электронный архив Напомню, что библиотека Adafruit GFX размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Однако и здесь есть проблема. Шрифт в файле glcdfont.c рассчитан на однобайтную кодировку букв, a Arduino IDE использует для русских букв двухбайтовую UTF-8. Тем не менее в русской кодировке UTF-8 прослеживается определенная законо-
216 Часть III. Практическое применение Arduino мерность, которая позволяет сделать преобразование из UTF-8 в однобайтовую русскую кодировку Windows-1251. В листинге 10.12 показана функция utf8rus(), которая получает исходную строку, символы с кодами ОхОО-Oxbf пропускает без изменения в выходную строку, а в оставшихся кодах отбирает русские буквы и перекодирует их. String utf8rus(String source) { int i,k; String target; unsigned char n; char m[2] = { '01, f\0' }; k = source.length(); i = 0; while (i < k) { n = source[i]; i++; if (n >= OxCO) { switch (n) { case OxDO: { n = source[i]; i++; if (n == 0x81) { n = 0xA8; break; } if (n >= 0x90 && n <= OxBF) n = n + 0x2F; break; } case OxDl: { n = source[i]; i++; if (n == 0x91) { n = 0xB8; break; } if (n >= 0x80 && n <= 0x8F) n = n + 0x6F; break; m[0] = n; target = target + String(m); } return target; Теперь скетч для «бегущей строки» на 4-разрядной светодиодной матрице примет вид, приведенный в листинге 10.13. // подключение библиотек #include <SPI.h> #include <Adafruit_GFX.h> #include <Max72xxPanel.h>
Глава 10. Использование дисплеев в проектах Arduino 217 II пин CS int pinCS = 10; // количество матриц по горизонтали int numberOfHorizontal = 15; // количество матриц по вертикали int numberOfVertical = 1; // создание объекта Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontal, numberOfVertical); It строка для вывода String text = "Ардуино проекты БХВ"; // текущее смещение от 0 int offset=32; // максимальное значение скорости int maxspeedl=100; // минимальное значение скорости int minspeedl=1000; void setup () { // яркость от 0 до 15 matrix.setIntensityG); void loop () { String textnew=utf8rus(text); // очистка экрана matrix.fillScreen(LOW); // вывод строки с позиции offset for ( int i = 0 ; i < textnew.length(); i++ ) { matrix.setRotation( i, 1 ); if(i*6+offset>(-6) && i*6+offset<32) { matrix.drawChar(i*6+offset, 0, textnew[i], HIGH, LOW, 1); matrix.write(); // задержка (скорость) int speedl=(analogRead(АО),0,1023,minspeedl,maxspeedl); delay(speedl); // изменение смещения offset=offset-l; //в начало - позиция 32 if(offset+text.length()*6==0) offset=32; String utf8rus(String source) { int i,k; String target;
218 Часть III. Практическое применение Arduino unsigned char n; char m[2] = { fOf, f\Of }; к = source.length(); i = 0; while (i < k) { n = source[i]; i++; if (n >= OxCO) { switch (n) { case OxDO: { n = source[i]; i++; if (n = 0x81) { n = 0xA8; break; } if (n >= 0x90 && n <= OxBF) n = n + 0x2F; break; } case OxDl: { n = source[i]; i++; if (n == 0x91) { n = 0xB8; break; } if (n >= 0x80 && n <= 0x8F) n = n + 0x6F; break; m[0] = n; target = target + String(m); } return target; Загружаем скетч в плату Arduino и наблюдаем русифицированную «бегущую строку», скорость ее «бега » регулируем потенциометром. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_10_i2 сопровождающего книгу электронного архива (см. приложение 2). 10.6.5. Матрица 16x16 на светодиодах WS2812 Матрица на светодиодах WS2812 представляет собой светодиодную ленту из последовательно соединенных между собой 256 светодиодов, которые расположены в виде матрицы 16x16. В чем отличие светодиодных лент WS2812 от других светодиодных лент? Лента WS2812 состоит из адресных светодиодов, а каждый такой светодиод состоит из RGB-светодиода и контроллера! Благодаря такой начинке у нас есть возможность управлять цветом любого светодиода ленты и создавать потрясающие эффекты. Для управления светодиодами ленты используется специальный протокол. На вход первого в цепочке светодиода WS2812 подается сигнал из прямоугольных импуль-
Глава 10. Использование дисплеев в проектах Arduino 219 сов частотой 400 или 800 КГц. Импульсы, в зависимости от скважности, кодируют О или 1 для одного бита информации. Длинный E0 мс) низкий уровень означает RESET или старт новой последовательности. Первая микросхема считывает 24 бита, в которых закодирован RGB-сигнал по трем каналам светодиодов, а остальные импульсы пропускает на выходную шину. Следующие 24 бита достаются второй микросхеме, и т. д. Всего каскадом может объединяться 1024 микросхемы, информация в которых может обновляться 30 раз в секунду. Схема подключения платы Arduino для управления лентой показана на рис. 10.33. +5ВЗА Рис. 10.33. Монтажная схема подключения ленты WS2812 к плате Arduino При этом необходимо обратить внимание на следующие моменты: 0 цифровой вход ленты идет напрямую на вход микроконтроллера, поэтому между ним и управляющим пином Arduino нужен токоограничиваюший резистор с номиналом 200-500 Ом. Без него возможно выгорание первого светодиода ленты; Я если между лентой и контроллером (Arduino) большое расстояние, т. е. соединяющие их провода длиннее 10-15 см, то сигнальный провод и «землю» нужно скрутить для защиты от наводок. Поскольку протокол связи у ленты достаточно скоростной (800 кГц), на него сильно влияют внешние наводки, а экранирование «земляной» скруткой поможет этого избежать; ? питание — один светодиод при максимальной яркости потребляет ток 20 мА. В одном светодиоде три цвета — итого 60 мА на диод. Вы вряд ли будете зажигать на полную яркость белого цвета все светодиоды, но Ъ-А А матрица может потреблять запросто.
220 Часть III. Практическое применение Arduino 10.6.6. Arduino-библиотека Adafruit Neopixel Самые популярные библиотеки для работы с лентами WS2812: FastLED и Adafruit NeoPixel. Библиотека FastLED поддерживает все версии Arduino и множество протоколов передачи данных. Язык программирования, на котором она написана, — чистый С. Библиотека Adafruit NeoPixel разработки компании Adafruit работает медленнее, обладает меньшими возможностями, но содержит в себе лишь самое нужное. Написана она на С, языке ассемблера и немного на Wiring и поддерживает всю линейку Arduino. Воспользуемся для нашего следующего проекта — постепенного заполнения матрицы одним цветом — библиотекой Adafruit NeoPixel. Итак, установим библиотеку Adafruit NeoPixel и загрузим скетч (листинг 10.14). Электронный архив Библиотека Adafruit NeoPixel размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). // подключение библиотеки #include <Adafruit_NeoPixel.h> // пин подключения #define PIN 6 // Parameter 1 = number of pixels in strip Arduino pin number (most are valid) pixel type flags, add together as needed: 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) 400 KHz (classic fvlf (not v2) FLORA pixels, WS2811 drivers) Pixels are wired for GRB bitstream (most NeoPixel products) Pixels are wired for RGB bitstream (vl FLORA pixels, not v2) AdafruitJSIeoPixel strip = Adafruit_NeoPixel B56, PIN, NEO_GRB + NEO_KHZ800) ; // цвета int colors[6][3]-{{150,0,0},{150,150,0}, {0,150,0},{0,150,150}, {0,0,150},{150,0,150}, }; // текущий цвет int color=0; void setup() { Serial.begin(9600); strip.begin(); strip, show(); // // // // // // Parameter 2 Parameter 3 NEO_ NEO_ NEO_ NEO KHZ800 KHZ400 _GRB RGB
Глава 10. Использование дисплеев в проектах Arduino 221 void loop () { // заполнение цветом с 0 по 255 светодиод for (int i = 0; i < 256; i++){ strip.setPixelColor(i,colors[color][0],colors[color][1],colors[color][2]); // вывести на ленту strip.show(); // задержка delayE0) ; } // стирание цвета for (int i = 255; i >=0; i—) { strip.setPixelColor(i,0,0,0); // вывести на ленту strip.show(); // задержка delayE0); } // выбираем другой цвет color=(color+1)% б; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\10\_i0_13 сопровождающего книгу электронного архива (см. приложение 2). 10.6.7. Графический аудиоспектроанализатор на матрице 16x16 светодиодов WS2812 Создадим на плате Arduino и семиполосном фильтре MSGEQ7 для графического эквалайзера проект аудиоспектроанализатора с визуализацией на матрице 16x16 светодиодов WS2812. Восьмивыводной чип MSGEQ7 способен из входного аудиосигнала выделить частотные полосы 63, 160, 400 Гц; 1, 2,5, 6,25 и 16 кГц (рис. 10.34). Управляется фильтр MSGEQ7 по двум цифровым входам: Reset и Strobe. После стартового импульса Reset достаточно подать семь стробирующих импульсов на линию Strobe, и в результате после каждого стробирующего импульса на выходе чостотноя хороктеристико 63 №0 400 Ю00 2500 6250 16000 Рис. 10.34. Частотная харрактеристика MSGEQ7 — семиполосный эквалайзер
222 Часть III. Практическое применение Arduino set 1 RESET «J j _n игигигитл_ги STROBE OUTPUT 63 Hz 160 Hz 400 Hz 1kHz 2.5 kHz 6.25 kHz 16kHz 63 Hz 160 Hz Рис. 10.35. Диафамма длительности импульсов микросхеммы MSGEQ7 Out будет появляться напряжение, пропорциональное содержанию одной из семи частотных полос в аудиосигнале (рис^ 10.35). Монтажная схема соединений для графического аудиоспектроанализатора показана на рис. 10.36. 5В4А 1 1 1\ J lOOnF Рис. 10.36. Монтажная схема соединений для фафического аудиоспектроанализатора
Глава 10. Использование дисплеев в проектах Arduino 223^ Приступим к написанию скетча. Уровень сигнала для 7 частот будем собирать в массиве arri [7], предварительно масштабируя для диапазона 0-16. После получения значений выводим данные на ленту (колонки 1-15, по 2 колонки на одну частоту). При этом цвет пиксела зависит от уровня сигнала (зеленый, желтый, красный). Содержимое скетча показано в листинге 10.15. // подключение библиотеки #include <Adafruit_NeoPixel. h> // пин подключения ленты tdefine PIN 6 Adafruit_NeoPixel strip = AdafruitJNeoPixelB56, PIN, NEO_GRB + NEOJKHZ800); // цвета для разных уровней int colors[16] [3]={{0,200, 0}, {0,200,0}, {0,150,0},{0,150,0}, {150,150,0},{150,150,0}, {200,200,0},{200,200,0}, {150,0,0},{150,0,0}, {150,0,0},{150,0,0}, {200,0,0},{200,0,0}, {200,0,0},{200,0,0} tdefine msg7RESET 12 idefine msg7Strobe 11 idefine msg7DCout 0 int arrl[7] = {0,0,0,0,0,0,Ob- void setup () { Serial.begin(9600) ; pinMode(msg7RESET, OUTPUT); pinMode(msg7Strobe, OUTPUT); strip.begin(); strip.show(); void loopO { // осуществляем сброс MSGEQ7 digitalWrite(msg7RESET, HIGH); delayMicrosecondsA); digitalWrite(msg7RESET, LOW); delayMicrosecondsG2);
224 Часть III. Практическое применение Arduino // семь частот - семь стробирующих импульсов for (int x = 0; х < 7; х++){ digitalWrite(msg7Strobe, LOW); // ждем установления значения 36 мкс delayMicrosecondsC6); // считываем значение с аналогового входа // преобразовываем диапазон 0-1024 к диапазону 0-16 arrl[x] = map(analogRead(AO), 5, 1023, 0, 16); setVals(x,arrl[x]); delayMicrosecondsA600); digitalWrite(msg7Strobe, HIGH); delayMicroseconds C6); } // вывести на ленту strip.show(); //в последовательный порт для контроля for(int i=0;i<7;i++) {Serial.print(" ")/Serial.print(arrl[i]);} Serial.println(); //// вывести пикселы для 1 частоты B колонки) // колонки 2-15 void setVals(int i,int valuel) { for(int j=0;j<16;j++) { if(valuel>=j) { strip.setPixelColor(A5-j)+(i*2+2)*16, colors[j][0],colors!j][1], colors[j][2]); strip.setPixelColor(j+(i*2+l)*16,colors[j][0],colors[j][1], colors[j][2]); } else // без цвета { strip.setPixelColor(A5-j)+(i*2+2)*16,0,0,0); strip.setPixelColor(j+(i*2+l)*16, 0,0,0); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\W\_W_14 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 11 Подключение к Arduino исполнительных устройств Исполнительные устройства — это элементы автоматики, создающие управляющее воздействие на объект управления. Они изменяют положение или состояние регулирующего органа объекта управления таким образом, чтобы управляемый параметр соответствовал заданному значению. Исполнительное устройство или механизм (actuator) преобразует электрическую энергию в механическую или в физическую величину для воздействия на управляемый процесс. Это могут быть световые и звуковые устройства, электромагнитные клапаны, моторы постоянного (DC) или переменного (АС) тока, сервоприводы, релейные системы и многое другое. Рассмотрим работу Arduino с некоторыми исполнительными устройствами на примерах. 11.1. Подключение к плате Arduino электромагнитного или твердотельного реле Реле— электрическое или электронное устройство (ключ), предназначенное для замыкания и размыкания различных участков электрических цепей. Изобрели собственно реле в далеком 1831 г. А уже в 1837 г. американский изобретатель Сэмюэл Морзе создал основанный на реле электромагнитный телеграфный аппарат. Сегодня реле широко используются практически во всех областях техники, существует огромное количество самых разнообразных их видов. Наиболее распространенными являются электромагнитные реле. Электромагнитное реле — электромеханическое устройство, замыкающее и (или) размыкающее механические электрические контакты при подаче в обмотку реле электрического тока, порождающего магнитное поле, которое вызывает перемещение ферромагнитного якоря реле, связанного механически с контактами, и последующее перемещение контактов коммутирует внешнюю электрическую цепь. Рассмотрим устройство реле на примере широко используемого совместно с платами Arduino реле фирмы SONGLE SRD-05VDC (рис. 11.1). Это реле управляется
226 • Часть III. Практическое применение Arduino напряжением 5 В и способно коммутировать постоянные токи до 10 А при напряжении 30 В и переменные токи до 10 А при напряжении 250 В. Реле имеет две раздельные цепи: цепь управления, представленную контактами А1 и А2, и управляемую цепь с контактами 1, 2 и 3 (рис. 11.2). Эти цепи электрически никак между собой не связаны. При протекании тока по обмотке катушки, включенной между контактами А1 и А2, металлический сердечник, размещенный внутри этой обмотки, намагничивается, и к нему притягивается подвижный якорь, соединенный с контактом 2. Контакты 1 и 3 неподвижны. Стоит отметить, что якорь подпружинен, и пока мы не пропустим ток через обмотку сердечника, якорь будет удерживаться прижатым к контакту 1. При подаче тока сердечник, как уже говорилось, превращается в электромагнит и притягивает якорь к контакту 3. При обесто- чивании пружина снова возвращает якорь к контакту 1. Рис. 11.1. Электромагнитное реле SRD-05VDC АД! 3l J1 А2| |2 Рис. 11.2. Схема электромагнитного реле SRD-05VDC С подключением реле к плате Arduino связана проблема — вывод платы не может обеспечить мощность, необходимую для нормальной работы катушки реле. Поэтому необходимо усилить ток — для чего следует включить в схему транзистор. Для усиления тока удобнее применять и-/?-и-транзистор, включенный по схеме с общим эмиттером (ОЭ), — как показано на рис. 11.3. При таком способе можно подключать нагрузку с напряжением питания большим, чем питание микроконтроллера. Резистор на базе транзистора— ограничительный, его номинал может варьироваться в широких пределах A-10 кОм),— в любом случае транзистор будет работать в режиме насыщения. Транзистор можно взять любой, лишь бы он был и-р-и-тйпа. Коэффициент его усиления практически не имеет значения. Выбирается транзистор по току коллектора (нужный нам ток) и напряжению «коллектор — эмиттер» (напряжение, которым запитывается нагрузка). Я, как правило, использую транзисторы С945. Для включения реле, подключенного по схеме с ОЭ, на вывод Arduino необходимо подать 1, для выключения — 0 (листинг 11.1).
Глава 11. Подключение к Arduino исполнительных устройств 227 1.5 кОт п f #12 V ЙИМ1 к Arduino НРН. (С945, С458 или ) Рис. 11.3. Схема подключения электромагнитного реле к плате Arduino (р-канальное управление) int relayPin =10; void setup () // подключение Arduino к выводу D10 pinMode(relayPin, OUTPUT); // настроить вывод как выход (OUTPUT) // the loop function runs over and over again forever void loop() - { digitalWrite(relayPin, HIGH); // включить реле delayE000); digitalWrite(relayPin, LOW); // выключить реле delayE000); Дпя подключения к плате Arduino можно использовать готовые модули, содержащие сразу несколько реле с необходимой обвязкой (рис. 11.4). Но в таких модулях обычно применяется я-канальное управление (рис. 11.5). При таком управлении i реле включается цодачей на вывод Arduino низкого уровня. ! Твердотельные реле (Solid State Relay, SSR) применяются в промышленном обору- I довании— там, где нужна большая надежность и малые габариты (рис. 11.6). Во всех твердотельных оптоэлектронных реле коммутация цепей нагрузки осуществляется бесконтактно— за счет управления встроенными полупроводниковыми элементами. Как правило, это тиристоры или симисторы (для коммутации пере-
228 Часть III. Практическое применение Arduim Рис. 11.4. Модуль из 4 реле 5V signal Рис. 11.5. Схема подключения электромагнитного реле к плате Arduino (п-канальное управление) Рис. 11.6. Твердотельное реле
Глава 11. Подключение к Arduino исполнительных устройств 229 менного тока) и транзисторы (для коммутации постоянного тока). Такой способ управления дает ряд преимуществ перед обычными электромагнитными реле. Так же как и в обычных реле, в твердотельных существует гальваническая развязка между напряжением катушки и напряжением на силовых контактах. Только в электромеханических реле это достигается за счет разнесения катушки и силовых контактов в пространстве, а в твердотельных — за счет использования оптрона, обеспечивающего оптическую развязку. Твердотельные реле при работе потребляют и теряют гораздо меньше энергии, имеют меньшие габариты, высокое быстродействие, гораздо более длительный срок службы — и все этоабсолютно бесшумно! Но есть у них и минусы — высокая цена. Для подключения твердотельного реле к плате Arduino минусовой контакт управляющей сети подсоединяется к «земле», а плюсовой— к цифровому выводу Arduino. Выходного тока контакта Arduino для срабатывания твердотельного реле вполне достаточно. К реле можно подключить лампочку, вентилятор или, например, электромагнитный клапан для полива растений в теплице и программно управлять этими устройствами изменением состояния на цифровых выводах Arduino. 11.2. Подключение к плате Arduino электродвигателя постоянного тока 11.2.1. Управление двигателем с помощью транзистора Электродвигатели постоянного тока применяются в роботах на колесных или гусеничных платформах. И начнем мы с самого простого способа управления ими — с помощью транзистора. Выводы Arduino, сконфигурированные как output, находятся в низкоимпедансном состоянии, могут отдавать в нагрузку 40 мА и не в состоянии обеспечить питание мощной нагрузки и большого напряжения. Одним из способов управления мощной нагрузкой является использование полевых MOSFET-транзисторов. MOSFET- транзистор — это ключ для управления большими токами при помощи небольшого напряжения (в отличие от биполярных транзисторов, управляемых током). Рассмотрим управление с выхода Arduino мощной нагрузкой— электродвигателем — с помощью MOSFET-транзистора. Для этого проекта нам понадобятся следующие компоненты: 0 плата Arduino Uno; 0 кабель USB; ? плата прототипирования; а транзистор MOSFETIRF540; 0 диод 1N4007;
230 Часть III. Практическое применение Arduino П потенциометр 10 кОм; ? электродвигатель постоянного тока (DC) с редуктором; ? блок питания 5 В 2 А; ? соединительные провода разных типов (согласно схеме). В представленной на рис. 11.7 монтажной схеме этого проекта для управления скоростью двигателя с платы Arduino мы воспользуемся ШИМ, обеспечивающей получение изменяющегося аналогового значения напряжения соответствующей обработкой цифровых сигналов. Регулирование скорости двигателя будет осуществляться с помощью потенциометра. 1 Рис. 11.7. Монтажная схема подключения электродвигателя к плате Arduino При наличии в схеме индуктивной нагрузки (электродвигатель, электромагнитный клапан и т. п.) рекомендуется защищать MOSFET-транзистор от напряжения самоиндукции с помощью защитного диода. Управление электродвигателем при помощи ШИМ без защитного диода может вызвать такие проблемы, как нагрев транзистора или его полный выход из строя, замедление вращения двигателя, потеря мощности и пр. Если же нагрузка в схеме активная: светодиод, галогенная лампа, нагревательный элемент и т. п., то защитный диод не обязателен. Содержимое скетча управления скоростью электродвигателя с платы Arduino показано в листинге 11.2. Загружаем этот скетч в плату Arduino и поворотом ручки потенциометра изменяем скорость вращения двигателя.
Гпава 11. Подключение к Arduino исполнительных устройств 231 II Выход для подключения MOSFET const int MOTOR=9; // Аналоговый вход АО для подключения потенциометра const int POT=0; // переменная для хранения значения потенциометра int valpot = 0; // переменная для хранения скорости двигателя mt speedMotor = 0; void setup () { // выход управления двигателем как OUTPUT pinMode(MOTOR,OUTPUT); } void loopO { // чтение данных потенциометра valpot = analogRead(POT); // масштабируем значение к интервалу 0-255 speedMotor=map(valpot,0,1023,0,255); // устанавливаем новое значение ШИМ analogWrite(MOTOR,speedMotor); // пауза delayA00); } Электронный архив Полный вариант рассмотренного скетча находится в папке examplesW1\J\1_O2 сопровождающего книгу электронного архива (см. приложение 2). 11.3. Управление двигателями с помощью драйвера Для управления электродвигателями с платы Arduino разработаны специализированные микросхемы— так называемые драйверы двигателей. Наиболее распространенным в среде пользователей Arduino является модуль драйвера L298 (рис. 11.8), Рис. 11.8. Модуль драйвера L298
232 Часть III. Практическое применение Arduino который позволяет управлять двумя двигателями постоянного тока и обеспечивает максимальную нагрузку до 2 А на каждый двигатель. Напряжение питания двигателей при этом может находиться в диапазоне от 5 до 35 В. Чередованием разноименных сигналов (высокий логический уровень или низкий) на парах выводов IN1-IN2 и IN3-IN4 задается направление вращения двигателей Выводы ENA (привязан к IN1 и IN2) и ENB (привязан к IN3 и IN4) отвечают за раздельное управление каналами. Для регулировки скорости двигателей на выводы ENA и ENB подается ШИМ-сигнал. В представленной на рис. 11.9 монтажной схеме проекта управления двумя двигателями с помощью модуля драйвера двигателей L298N направление вращения каждого двигателя и его скорость регулируются потенциометрами: первый потенциометр устанавливает направление движения и скорость для первого двигателя, второй— для второго. Среднее положение потенциометров— нулевая скорость. Осуществляется это следующим программным блоком: // чтение данных потенциометра valpotl = analogRead(POTl); // масштабируем значение к интервалу -255 - 255 speedMotorl=map(valpotl,0,1023,-255,255); if(speedMotorl<512) { digitalWrite(INI,HIGH); digitalWrite(IN2,LOW); } else { digitalWrite(INI, LOW); digitalWrite(IN2 HIGH,); } analogWrite(ENA,speedMotorl); Рис. 11.9. Монтажная схема подключения электродвигателей к плате Arduino с помощью модуля драйвера двигателей L298N
Fnaea 11. Подключение к Arduino исполнительных устройств 233 Содержимое скетча для управления скоростью двигателей с платы Arduino показано в листинге 11.3. Загружаем этот скетч в плату Arduino и поворотами ручек потенциометров изменяем скорость и направление вращения для каждого двигателя. Что любопытно — два двигателя как раз и позволяют создать движущуюся платформу. // Контакты подключения к модулю драйвера L298 const int ENA=10; const int IN1=9; const int IN2=8; const int ENB=5; const int IN3=7; const int IN4=6; // Аналоговые входы для подключения потенциометров const int POT1=0; const int POT2=1; // переменные для хранения значений потенциометров int valpotl = 0; int valpot2 = 0; // переменные для хранения скоростей двигателей int speedMotorl = 0; int speedMotor2 = 0; void setup () { // выходы как OUTPUT pinMode(ENA,OUTPUT); pinMode(INI,OUTPUT); pinMode(IN2,OUTPUT); pinMode(ENB,OUTPUT); pinMode(IN3,OUTPUT); pinMode(IN4,OUTPUT); void loop() { // чтение данных потенциометра 1 valpotl = analogRead(POTl); // масштабируем значение к интервалу -255 - 255 speedMotorl=map (valpotl, 0,1023, -255,255) ; if(speedMotorl<512) { digitalWrite(INI,HIGH); digitalWrite(IN2,LOW); } else { digitalWrite(INI, LOW);
234 Часть III. Практическое применение Arduina digitalWrite(IN2 HIGH,); } // чтение данных потенциометра 2 valpotl = analogRead(POT2); // масштабируем значение к интервалу -255 - 255 speedMotor2=roap(valpot2,0,1023,-255, 255); if(speedMotor2<512) { digitalWrite(IN3,HIGH); digitalWrite(IN4,LOW); } else { digitalWrite(IN3, LOW); digitalWrite(IN4 HIGH,); } analogWrite(ENB,speedMotor2); // пауза delayA00); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\11\_11_03 сопровождающего книгу электронного архива (см. приложение 2). 11.4. Подключение к плате Arduino сервопривода Сервопривод это устройство, которое обеспечивает преобразование сигнала в строго соответствующее этому сигналу перемещение (поворот) исполнительного устройства. Представляет он собой прямоугольную коробку с мотором, схемой и редуктором внутри, а также выходным валом, который должен поворачиваться на строго фиксированный угол, определяемый входным сигналом. Существует очень много видов сервоприводов, которые различаются габаритами, материалом шестеренок (пластмасса, металл), способом управления (аналоговые и цифровые), скоростью вращения вала, крутящим моментом, диапазоном поворота A20, 180°, непрерывного вращения) и пр. Сервопривод управляется с помощью импульсов переменной длительности — угол поворота его выходного вала определяется длительностью импульса, подаваемого по сигнальному проводу (как можно видеть, широтно-импульсная модуляция задействована и здесь). Сервопривод ожидает импульса каждые 20 мс. Длительность импульса определяет, в какое положение должен повернуться вал. Например, импульс в 1,5 мс диктует приводу поворот в положение 90° (нейтральное положение). Когда сервопривод получает команду на перемещение, его управляющий орган перемещается в это положение и удерживает его. Если внешняя сила действует на сервопривод, когда он удерживает заданное положение, сервопривод будет сопро-
Fnaea 11. Подключение к Arduino исполнительных устройств 235 тивляться перемещению из этого положения. Максимальная величина силы, которую может выдерживать сервопривод, характеризует вращающий момент сервопривода. Однако сервопривод не навсегда удерживает свое положение — импульсы позиционирования должны повторяться, информируя сервопривод о сохранении положения. Рис. 11.10. Комплект сервопривода MG995 Рассмотрим подключение к плате Arduino популярного сервопривода MG995 (рис. 11.10). Для этого нам понадобятся три провода (рис. 11.11): ? красный провод— питание (внешний стабилизированный источник питания 4,8-7,2 В); ? черный провод — к выводу Arduino GND; О синий — сигнальный, подключается к цифровому ШИМ-выводу платы Arduino. Для управления сервоприводами с помощью Arduino в Arduino ГОЕ имеется стандартная библиотека Servo, поддерживающая функции для установки настроек сервопривода — необходимого угла поворота и считывания состояния. В листинге 11.4 представлен скетч вращения сервопривода от 0 до 120° с шагом 1°, а затем в обратную сторону. После загрузки этого скетча в плату Arduino вал сервопривода медленно вращается сначала в одну, затем в другую сторону. // подключение библиотеки Servo ^include <Servo.h> // создать объект servo Servo servol;
236 Часть III. Практическое применение Arduino // пин для подключения сервопривода const int pin_servo=9; // макс, значение угла поворота const int angle_max=120; // для хранения текущей позиции сервопривода int angle = 0; // для хранения направления 1 или -1 dir=l; void setup() { // подключить управление сервоприводом к пину pin_servo servol.attach(pin_servo); void loop() { for(;;) { // команда установки положения сервопривода servol.write(angle); // время на перемещение сервопривода delayA5); // if(angle==0) dir=l; else if (angle=angle_max) dir=-l; angle=angle+dir; 4.8 - 7.2 В Рис. 11.11. Монтажная схема подключения сервопривода к плате Arduino
Fnaea 11. Подключение KArduino исполнительных устройств 237^ 11.4.1. Использование сервопривода в проекте звуковой сигнализации Сервоприводы очень часто применяются в робототехнике, где вообще характерно использование различных двигателей, сервоприводов и датчиков, и здесь мы рассмотрим, как с помощью сервопривода можно оснастить робота звуковой сигнализацией о наличии препятствий на пути его движения. Датчик расстояния — модуль НС SR-04 — находится на вращающейся в горизонтальной плоскости платформе робота (вращение платформы как раз и осуществляется с помощью сервопривода) и измеряет расстояние до преграды. При обнаружении объекта, находящегося на расстоянии менее 1 м от модуля, платформа останавливается и подает звуковой сигнал на динамик до тех пор, пока объект не удалится на большее расстояние. Для этого проекта (рис. 11.12) нам понадобятся следующие компоненты: ? плата Arduino Uno; О ультразвуковой модуль НС SR-04; ? сервопривод; CJ платформа для крепления сервопривода и модуля НС SR-04; О динамик 8 Ом; ? резистор 500 Ом; ? транзистор КТ503е; ? источник внешнего питания 5 В (для питания сервопривода); ? соединительные провода. Рис. 11.12. Монтажная схема подключения к плате Arduino сервопривода, ультразвукового модуля НС SR-04 и динамика
238 Часть III. Практическое применение Arduim Содержимое скетча, обеспечивающего работу этого проекта, показано в листинге 11.5. Сервопривод вращает платформу с датчиком от 0 до 180° и обратно. На каждом шаге система проверяет показания модуля расстояния НС SR-04. Если объект нарушает границу (значение distdetect, равное 100 см), на динамик подается звуковой сигнал: tone (PIN_SPEAKER, FREQ) ; Далее система ждет, пока объект не выйдет из «зоны поражения», после чего отключает динамик и продолжает вращение платформы. // константы для выводов #define PIN_TRIG 12 #define PIN_ECHO 13 #define PIN_SERVO 9 #define PIN_SPEAKER 8 // расстояние обнаружения, см #define DISTJDETECT 100 // частота звукового сигнала tdefine FREQ 546 // подключение библиотеки для НС SR04 #include "Ultrasonic.h" // создание объекта Ultrasonic // Trig - 12, Echo - 13 Ultrasonic ultrasonic(PINJTRIG, PIN_ECHO); // переменная для хранения измеренного расстояния float dist_cm=0; // подключение библиотеки для серво #include <Servo.h> // создание объекта Servo Servo myservo; // переменная для хранения позиции сервопривода int pos =0; // переменная направления перемещения сервопривода int dir=l; void setup() { // запуск последовательного порта Serial.begin(9600); // запуск серво на выходе PIN_SERVO myservo.attach(PINJSERVO); void loop() { // вычисление следующей позиции сервопривода pos=pos+dir;
Глава 11. Подключение KArduino исполнительных устройств 239 II установить сервопривод в позицию pos myservo.write(pos); // при достижении крайних позиций изменить // направление dir if(pos==180) dir=-l; else if(pos==0) else ; // получить данные с дальномера dist_cm = ultrasonic.Ranging(CM); Serial.printIn(dist_cm); hi обнаружение объекта в зоне if (dist_cm>0 && dist_cm<DIST_DETECT) { // звуковой сигнал tone(PIN_SPEAKERf FREQ); } // пока в зоне обнаружения while (dist_cm>0 && dist_cm<DIST_DETECT) { dist_cm = ultrasonic.Ranging(CM); Serial.printIn(dist_cm); } // отключить звуковой сигнал noTone (PINJSPEAKER) ; // пауза перед сменой позиции сервопривода delayE0); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\1i\_11JM сопровождающего книгу электронного архива (см. приложение 2). 11.5. Подключение к плате Arduino шагового двигателя Шаговые двигатели представляют собой электромеханические устройстэа, задачей которых является преобразование электрических импульсов в перемещение вала двигателя на определенный угол. Достоинствами шаговых двигателей по сравнению с обычными являются: ? высокая точность позиционирования и повторяемости — качественные шаговые двигатели имеют точность не хуже 2,5% от величины шага, при этом ошибка не накапливается при последующих шагах; ? шаговый двигатель может быстро стартовать, останавливаться и выполнять реверс;
240 Часть III. Практическое применение Arduino П четкая взаимосвязь угла поворота ротора от количества входных импульсов (в штатных режимах работы) позволяет выполнять позиционирование без применения обратной связи; ? шаговые двигатели обеспечивают получение сверхнизких скоростей вращения вала без использования редуктора; ? шаговые двигатели работают в широком диапазоне скоростей, поскольку скорость напрямую зависит от количества входных импульсов. Шаговые двигатели применяются там, где требуется высокая точность перемещений,— например, в принтерах, факсах и копировальных машинах, в станках с ЧПУ, в ЗО-принтерах. Минимально возможный угол перемещения шагового двигателя называется шагом, Управление шаговым двигателем сводится к задаче отработать определенное число шагов в нужном направлении и с нужной скоростью. Для управления шаговыми двигателями используют специальные устройства — драйверы шаговых двигателей. Популярный драйвер шагового двигателя А4988 (рис. 11.13) работает от напряжения 8-35 В и может обеспечить ток до 1 А на фазу без радиатора (и до 2 А с радиатором). Он имеет защиту от перегрузки и перегрева. Одним из параметров шаговых двигателей является количество шагов на один полный оборот C60°). Например, для шаговых двигателей Nema 17 (рис. 11.14)— это 200 шагов на оборот, т.е. 1 шаг равен 1,8°. Драйвер А4988 позволяет увеличить это значение за счет управления промежуточными шагами и имеет пять режимов микрошага: 1 (полный), V2, 74, V8 и Лб шага. Назначение контактов (выводов) драйвера А4988 приведено на рис. 11.15: ? ENABLE — включение/выключение драйвера; ? MSI, MS2, MS3 — контакты для установки микрошага; Рис. 11.13. Драйвер шаговых двигателей А4988 Рис. 11.14. Шаговый двигатель Nema 17
Глава 11. Подключение кАгди'то исполнительных устройств 241 О RESET — сброс микросхемы; ? STEP— генерация импульсов для движения двигателей (каждый импульс — шаг), с его помощью можно регулировать скорость двигателя; ? DIR — установка направления вращения; ? VMOT — питание для двигателя (8-35 В); ? GND — общий; ? 2В, 2 А, 1 А, 1В — подключение обмоток двигателя; ? VDD — питание микросхемы C,5-5 В). А4988 3LE MS1 MS2 MS3 С RESET SLEEP —*STEP Рис. 11.15. Выводы драйвера А4988 Значение микрошага устанавливается комбинацией сигналов на входах MSI, MS2 и MS3. Есть пять вариантов дробления шага (табл. 11.1). MS1 0 1 0 1 1 Таблица 11.1 MS2 0 0 1 1 1 . Комбинация значений для выбора микрошага MS3 0 0 0 0 1 Дробление шага 1 1/2 1/4 1/8 1/16 Для работы в режиме микрошага необходим слабый ток. На модуле А4988 ограничение тока осуществляется с помощью находящегося на плате потенциометра. Драйвер очень чувствителен к скачкам напряжения по питанию двигателя, поэтому производитель рекомендует устанавливать для сглаживания таких скачков электролитический конденсатор большой емкости. Имейте также в виду, что подключение или отключение шагового двигателя при включенном драйвере может привести к выходу двигателя из строя.
242 Часть III. Практическое применение Arduino 11.5.1. Управление дроблением шага и направлением вращения шагового двигателя с платы Arduino Создадим проект управления дроблением шага и направлением вращения шагового двигателя с платы Arduino. Для этого нам понадобятся следующие компоненты: ? плата Arduino Uno; ? драйвер А4988; ? шаговый двигатель Nema 17; ? потенциометр 10 кОм; ? кнопка; ? переключатель 2-позиционный; ? резистор 10 кОм — 3 шт.; ? провода разных типов (согласно схеме). Монтажная схема этого проекта представлена на рис. 11.16. Рис. 11.16. Монтажная схема подключения компонентов для управления дроблением шага и направлением вращения шагового двигателя Содержимое скетча, обеспечивающего работу этого проекта, показано в листинге 11.6. Нажатие на кнопку включает/выключает двигатель подачей сигнала low/high на вход ENABLE драйвера А4988. С помощью переключателя мы выбираем направление вращения двигателя (сигнал с переключателя подается напрямую
Глава 11. Подключение к Arduino исполнительных устройств 243 на вход DIR драйвера А4988), а с помощью потенциометра — один из режимов микрошага: 1, V2, V4, V8, Vi6. // пины для подключения контактов STEP, DIR const int STEP 3 int DIR 2 // для регулировки скорости - пин потенциометра tdefine POT АО // для кнопки tdefine BUTTON 9 // для включения/выключения fdefine EN 8 // количество шагов на 1 оборот idefine ROUND 200 // скорость двигателя tdefine SPEED 10 // массив пинов для MS1,MS2,MS3 int pins_steps[]={7,6,5}; int steps[5][3]={" {0,0,0}, // 1 {1,0,0}, // 1/2 {0,1,0}, // 1/4 {1,1,0}, // 1/8 {1,1,1} // 1/16 // для кнопки int prevB=0; int tekB=0; boolean movement=false; void setup() { // режим для выводов STEP и DIR как pinModeSTEP, OUTPUT); pinMode(DIR, OUTPUT); // начальные значения digitalWrite(STEP, 1); digitalWrite(DIR, 0); // режим для enable pinMode(EN, OUTPUT);
244 Часть III. Практическое применение Arduino //не разрешать digitalWrite(EN, 1); // для MS1,MS2,MS3 for(int i=0;i<3;i++) { pinMode(pins_steps[i], OUTPUT); void loop() { // получить режим микрошага digitalWrite(DIR, 1); int mode=map(analogRead(POT),0,1024,0,5); // установить for(int i=0;i<3;i++) { digitalWrite(pins_steps[i], steps[mode][i]); } // сделать 1 оборот if (movement===true) { digitalWrite(STEP, 1) ; delay(SPEED); digitalWrite(STEP, 0); delay(SPEED); } // проверка нажатия кнопки tekB = debounce(prevB, BUTTON); if (prevB = 0 && tekB ==1) { movement=! movement ; digitalWrite (EN, Imovement) ; } prevB = tekB; } // проверка на дребезг int debounce(int prev,int pin) { int tek = digitalRead(pin); if (prev != tek) { delayE); tek = digitalRead(pin); return tek; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\11\_ii_06 сопровождающего книгу электронного архива (см. приложение 2).
Глава 11. Подключение к Arduino исполнительных устройств 245 11.6. Подключение к плате Arduino бесколлекторного двигателя Бесколлекторные двигатели (рис. 11.17) используются в различных радиоуправляемых летающих моделях. По сравнению с коллекторными бесколлекторные двигатели эффективно работают в более широком диапазоне оборотов и имеют более высокий КПД. Конструкция двигателя при этом проще— в ней нет щеточного узла, который работает постоянно в режиме трения, создает искры и в итоге провоцирует потерю энергии. Бесколлекторные двигатели питаются трехфазным переменным током, поэтому для их работы необходим специальный контроллер (регулятор скорости), преобразующий постоянный ток от аккумуляторных батарей в переменный (рис. 11.18). Рис. 11.17. Бесколлекторный двигатель Рис. 11.18. Регулятор ESC для бесколлекторного двигателя Регулятор ESC, обеспечивающий работу бесколлекторного двигателя, имеет три разъема для подключения собственно двигателя, два провода для подключения питания (обычно это литий-полимерная батарея) и 3-контактный разъем для под-
246 Часть III. Практическое применение Arduino ключения к управляющему устройству (например, к плате Arduino). При этом для управления задействуются два контакта: черный — GND и белый — управляющий, а третий (красный — 5 В) можно использовать для питания платы. Регулятор последовательно переключает обмотки бесколлекторного двигателя с определенной частотой. Управляя частотой переключения обмоток, мы управляем скоростью вращения ротора. При программировании используется библиотека Servo: #include <Servo.h> Servo motor; // Инициализация мотора motor.attach(motor__pin, js_position, max_position); где: ? motorpin — контакт (пин) подключения; ? j sjposition — начальная позиция A500); ? max^position — максимальное значение B300). Диапазон значений, подаваемых на регулятор, должен находиться в интервале 800- 2300. Содержимое скетча, обеспечивающего управление скоростью бесколлекторного двигателя с платы Arduino вращением потенциометра, показано в листинге 11.7. #include <Servo.h> Servo motor; // пин подключения мотора int motor_pin = 6; // Начальная позиция, всегда 1.5 мс для регуляторов бесколлекторных двигателей int jsjposition = 1500; // Максимальное значение ШИМ 2.3 мс int max_position = 2300; // Минимальное значение ШИМ 0.8 мс int minjposition = 800; void setup() { // Инициализация мотора motor.attach(motor_pin, js_position, maxjposition); // Начальная установка регулятора в нулевое положение motor.writeMicroseconds(jsjposition); delayG00);
Глава 11. Подключение кАгбшпо исполнительных устройств 247 void loop () { // Считывание положения потенциометра js_position = analogRead(АО); // Преобразование положения потенциометра js_position = map(js_position, О, 1023, 800, 2300); motor.writeMicroseconds(js_position); // Задержка 20 мс delayB0); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i1\__H_07 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 12 Arduino и беспроводная связь Беспроводная передача данных (беспроводная связь) — связь, которая осуществляется в обход проводов по радиоканалу. В мире Arduino существует множество устройств беспроводной связи. Здесь мы рассмотрим некоторые из них. 12.1. ИК-управление Устройства инфракрасного (ИК) диапазона волн часто применяются в робототехнике, поскольку на сравнительно недорогих ИК-приемопередатчиках можно организовать вполне полноценный обмен данными. Самое простое их применение — использование ИК-пульта для управления роботом. При этом пульт служит в качестве передатчика. Для приема сигнала с пульта используется специальный ИК-приемник — датчик, воспринимающий инфракрасный сигнал только на определенной частоте C0, 33, 36, 38, 40 кГц) и игнорирующий посторонние световые шумы от ламп освещения и солнца. В своих проектах мы можем задействовать любой пульт и соответствующий по частоте приемник, а можно приобрести и комплект (рис. 12.1). Рис. 12.1. Комплект в составе ИК-пульта и ИК-приемника
Глава 12. Arduino и беспроводная связь 249 Рис. 12.2. Монтажная схема подключения ИК-приемника к плате Arduino Монтажная схема подключения ИК-приемника к плате Arduino показана на рис. 12.2. Библиотека для работы с ИК-приемником, поставляемая в составе Arduino IDE, не содержит примеров. Чтобы посмотреть и попробовать примеры работы с ИК- приемником, необходимо установить расширенную библиотеку. Но сначала необходимо удалить встроенную библиотеку. Для этого заходим в каталог установки Arduino IDE и из каталога libraries удаляем папку RobotlRRemote. Затем с сайта https://github.com/z3tO/Arduino-IRremote скачиваем архив, содержащий обновленную библиотеку IRemote, и устанавливаем ее в Arduino IDE командой меню Эскиз | Include library | Add ZIP library. Электронный архив Библиотека IRemote размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). После установки расширенной библиотеки в Arduino ГОЕ появятся примеры. Выберем пример IRRecvDemo (рис. 12.3) и загрузим его скетч в плату Arduino, предварительно поменяв контакт подключения приемника на D2, как показано на схеме подключения (см. рис. 12.2). Содержимое исправленного скетча показано в листинге 12.1. // подключение библиотеки linclude <IRremote.h> // пин подключения приемника int RECV_PIN = 2; // создаем объект ИК-приемника IRrecv irrecv(RECVJPIN); // создаем структуру результата приема данных decode results results;
250 Часть III. Практическое применение Arduino void setup() { // запустить последовательный порт Serial.begin(9600); // запустить приемник irrecv.enableIRIn(); void loop() { // если данные получены if (irrecv.decode(&results)) { // вывод кода в последовательный порт Serial.println(results.value, HEX); irrecv.resume(); // ждать следующее нажатие Рис. 12.3. Пример IRRecvDemo в списке библиотек Arduino IDE
Глава 12. Arduino и беспроводная связь 251 Загрузив этот скетч в плату Arduino, мы можем получить коды клавиш (табл. 12.1), которыми воспользуемся в дальнейшем в примере управления сервоприводом с ИК-пульта (см. разд. 12.1.1). Возможно, для вашего пульта будут актуальны другие коды. Таблица 12.1. Коды клавиш пульта для управления Кнопка пульта —> ок t i * Код FF22DD FFC23D FF02FD FF629D . FFA857 FF42BD Действие Вращение влево Вращение вправо Стоп В крайнее левое В крайнее правое В среднее положение 12.1.1. Управление сервоприводом с помощью ИК-связи Итак, подключим к плате Arduino ИК-приемник и сервопривод (монтажная схема этого проекта представлена на рис. 12.4) и напишем скетч (листинге 12.2), предусматривающий, что коды, отправляемые ИК-пультом, принимаются ИК-датчиком и анализируются скетчем на совпадение с кодами из таблицы. При совпадении вызываются соответствующие подпрограммы вращения вала сервопривода. Загружаем этот скетч в плату Arduino и управляем движением сервопривода с помощью пульта. Рис. 12.4. Монтажная схема подключения ИК-приемника и сервопривода к плате Arduino
252 Часть III. Практическое применение Arduino // Константы для кодов пульта #define KOD_POS0 0xFF629D #define KOD_POS180 0xFFA857 #define KOD_LEFT 0xFF22DD #define KOD_RIGHT 0xFFC23D #define KOD_STOP 0xFF02FD #define KOD_POS90 0xFF42BD // подключение библиотеки IRremote #include <IRremote.h> // пин подключения приемника const int RECV_PIN = 2; // создаем объект ИК-приемника IRrecv irrecv(RECV_PIN); // создаем структуру результата приема данных decode_resuits results; // подключение библиотеки Servo #include <Servo.h> // создать объект servo Servo servol; // пин для подключения сервопривода const int pin_servo=9; // для хранения текущей позиции сервопривода int angle = 0; // для хранения направления 1 или -1 int dir=l; // движение 1, останов 0 int go=l; void setup() { // подключить управление сервоприводом к пину pin_servo servol.attach(pin_servo); // запустить приемник irrecv.enablelRIn(); void loopO { if (irrecv.decode(&results)) { // проверка кода и выбор действия switch(results.value) { case KOD POSO:
Глава 12. Arduino и беспроводная связь 253 go=0;angle=O;go_pos(angle); break; case KOD_POS180: go=180;angle=0;go_pos(angle); break; case KOD_POS90: go=90;angle=0;gojpos(angle); break; case KOD_LEFT: dir=-l;go=l; break; case KOD_RIGHT: dir=l;go=l; break; case KOD_STOP: go=0; break; default: break; } // ждать следующее нажатие irrecv.resume(); } // если серво движется if(go==l) { angle=angle+dir; go_pos(angle); //в крайних позициях - стоп if(angle==0 || angle==180) go=0; // установить в позицию void go_pos(int ang) { // команда установки положения сервопривода servol.write(ang); // время на перемещение сервопривода delayA5); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\12\_i2_02 сопровождающего книгу электронного архива (см. приложение 2).
254 Часть III. Практическое применение Arduino 12.2. Радиомодули для частоты 433 МГц Еще одним широко распространенным среди разработчиков способом организации беспроводной связи в проектах Arduino является использование радиомодулей, работающих на частоте 433,920 МГц. Частота 433,920 МГц выделена для работы маломощных цифровых передатчиков, например: радиобрелоков автосигнализации, брелоков управления шлагбаумами, радиолюстрами, радиорозетками, радиомоделями. Радиомодули передатчика FS1000A (рис. 12.5) и приемника MX-RM-5V (рис. 12.6) позволяют организовать радиосвязь на расстоянии до 100 м. Данные при этом передаются только в одном направлении. Для полноценной работы к модулю передатчика необходимо припаять антенну. Рекомендуемая длина антенны для передатчиков с частотой 433 МГц равна 17 см. DATA +5B 3-12B / DATA Рис. 12.5. Модуль радиопередатчика FS1000A Рис. 12.6. Модуль радиоприемника MX-RM-5V 12.2.1. Управление светодиодом платы Arduino с другой такой же платы по радиоканалу 433 МГц Подсоединим приемник к первой плате Arduino, передатчик — ко второй и попробуем управлять включением/выключением светодиода на второй плате Arduino нажатием кнопок, подсоединенных к первой плате Arduino. При программировании подключим библиотеку RCSwitch. Электронный архив Библиотека RCSwitch размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Содержимое скетча для первой платы Arduino (с двумя кнопками и передатчиком FS1000A) показано в листинге 12.3.
Глава 12. Arduino и беспроводная связь 255 11 подключение библиотеки tinclude <RCSwitch.h> // создание объекта RCSwitch mySwitch = RCSwitch () ; // пины для подключения кнопок int pinButtons []={ 10,11} ; // для сохранения предыдущих состояний кнопок int lastButtons [ ] = {0, 0} ; // для сохранения текущих состояний кнопок int currentButtons [ ] = {0,0}; void setup () { // активировать передатчик // пин подключения вывода Data 12 mySwitch.enableTransmitA2); void loop () { // проверка нажатия кнопок выбора программ for(int i=0;i<2;i++) { // борьба с дребезгом currentButtons [i] = debounce(lastButtons [i],pinButtons [i]); if (lastButtons [i] — 0 && currentButtons [i] == 1) // если нажатие. { doButtons(i); } lastButtons[i] = currentButtons[i]; // обработка нажатия клавиш void doButtons(int but) { switch(but) { case 0: mySwitch.send(B0100, 4); break; case 1: mySwitch.send(BOlOO, 4); break; default: break;
256 Часть III. Практическое применение Arduino I/ Функция сглаживания дребезга // Принимает в качестве аргумента предыдущее состояние кнопки //и выдает фактическое. int debounce(int last,int pinl) { int current = digitalRead(pinl); // Считать состояние кнопки if (last != current) // если изменилось... { delayE); // ждем 5 мс current = digitalRead(pinl); // считываем состояние кнопки return current; // возвращаем состояние кнопки В цикле loop о ждем нажатия кнопки и с помощью функции mySwitch.sendo отправляем сообщение по радиоканалу. В параметрах функции указываются сообщение для отправки и размер сообщения в битах. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\12\_12JK сопровождающего книгу электронного архива (см. приложение 2). Содержимое скетча для второй платы Arduino (с приемником MX-RM-5V) показано в листинге 12.4. // подключение библиотеки #include <RCSwitch.h> // создание объекта RCSwitch mySwitch = RCSwitch(); // пин для подключения светодиода int pinLed=13; void setup() { // вывод светодиода как OUTPUT pinMode(pinLed, OUTPUT ); // инициализация приемника // Используется прерывание 0 (вывод 2) mySwitch.enableReceive@); void loop() { if (mySwitch.available()) { int value = mySwitch.getReceivedValue(); if (value — B1000) digitalWrite(pinLed, HIGH); // включить
Гпава 12. Arduino и беспроводная связь 257 else if (value == В0100) digitalWrite(pinLed, LOW); // выключить mySwitch.resetAvailable(); Электронный архив* Полный вариант рассмотренного скетча находится в папке examples\12\_12_04 сопровождающего книгу электронного архива (см. приложение 2). Приемник MX-RM-5V критичен даже к небольшим пульсациям на шине питания. Если Arduino управляет устройствами, вносящими даже небольшие, но постоянные пульсации в шину питания, то приемник расценивает эти пульсации как сигнал. Влияние пульсаций на приемник можно снизить установкой на шине питания приемника сглаживающего конденсатора или использованием для приемника отдельного стабилизированного источника питания. 12.3. Радиомодули NRF24L01 Еще одним — очень популярным и бюджетным — вариантом соединения двух Arduino-устройств по радиоканалу является использование беспроводных модулей NRF24L01 (рис. 12.7). Малое энергопотребление, достойный радиус действия, высокая скорость передачи и низкая цена— вот основные качества радиомодуля NRF24L01. Рис. 12.7. Радиомодуль NRF24L01 Радиомодуль NRF24L01 представляет собой полудуплексное устройство: в один момент времени оно может либо передавать, либо принимать информацию. Чтобы обеспечить двустороннюю связь, нужно постоянно переключаться с одного режима на другой. Модуль передает данные на частоте от 2,4 до 2,525 ГТц — в зависимости от выбранного канала, которых в NRF24L01 доступно 126. Используется модуляция GFSK. Модуль подключается к Arduino по интерфейсу SPI. Один модуль способен поддерживать связь сразу с шестью приемниками или передатчиками, т.е. можно объединить сразу семь устройств в общую радиосеть на частоте
258 Часть III. Практическое применение Arduino 2,4 ГТц. Максимальная скорость обмена данными между модулями составляет 2Мбит/с. Дальность передачи— до 100 м. Если вам нужно большее расстояние передачи, можно использовать модуль с внешней антенной (рис. 12.8), при этом расстояние передачи может составить до 1000 м со скоростью 250 Кбит/с (или 500 м со скоростью до 2 Мбит/с). Рис. 12.8. Радиомодуль NRF24L01 с внешней антенной Как уже отмечалось, радиомодуль NRF24L01 подключается к плате Arduino при помощи SPI-интерфейса. При этом, в зависимости от используемой библиотеки, может задействоваться и дополнительный выход прерывания IRQ. Но в нашем примере мы обойдемся без него. Схема соединения контактов радиомодуля NRF24L01 и платы Arduino выглядит следующим образом (табл. 12.2). Таблица 12.2. Схема Радиомодуль NRF24L01 Arduino Uno соединения контактов радиомодуля NRF24L01 GND GND VCC +3,3 В СЕ 9 CSN 10 MOSI 11 и платы Arduino MISO 12 SCK 13 Монтажная схема подключения радиомодуля NRF24L01 к плате Arduino показана на рис. 12.9. Питание модуля— 3,3 В, но выводы модуля толерантны и к 5 В. На многих платах Arduino имеется встроенный стабилизатор напряжения на 3,3 В, но он, однако, не обладает достаточной мощностью для правильной работы NRF24L01. Особенно эта проблема актуальна для Arduino Mega. В связи с этим для питания радиомодулей NRF24L01 рекомендуется использовать внешние стабилизаторы.
Глава 12. Arduino и беспроводная связь 259 Рис. 12.9. Монтажная схема подключения радиомодуля NRF24L01 к плате Arduino 12.3.1. Организация связи между двумя платами Arduino с использованием модулей NRF24L01 Установим связь между двумя платами Arduino с использованием модулей NRF24L01. При этом к первой плате подсоединим датчик влажности и температуры DHT11 (монтажная схема соединений для этой платы показана на рис. 12.10). Каждые 30 с отправляем данные этого датчика по радиоканалу, кроме того, каждые 5 с проверяем соединение— отправляем данные и ждем ответа. При отсутст- Рис. 12.10. Монтажная схема подключения модуля NRF24L01 и датчика DHT11 к плате Arduino
260 Часть III. Практическое применение Arduino вии ответа сигнализируем светодиодом на выходе 13 о проблемах с соединением. Для взаимодействия Arduino с модулями NRF24L01 подключим библиотеку RF24. Электронный архив Библиотека RF24 размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Содержимое скетча для первой платы Arduino показано в листинге 12.5. // пауза отправки данных для контроля связи #define PAUSE_SEND_STATUS 5000 // максимальное время ожидания ответа от NRF2401 #define TIME_WAIT_STATUS 500 // пауза отправки данных текущих температуры и влажности #define PAUSE_SEND_DATA 30000 // идентификатор канала #define ID 0x357340 // номер канала #define CHANNEL 5 // нулевой байт #define TEMP 56 // температура текущая #define HUMIDITY 57 // влажность текущая #define STAT 59 // проверка статуса сети // Подключаем библиотеку для работы с шиной SPI #include <SPI.h> // Подключаем файл настроек из библиотеки RF24 #include <nRF24L01.h> // Подключаем библиотеку для работы с NRF24L01 #include <RF24.h> // Создаем объект radio для работы с библиотекой // RF24, указывая номера выводов nRF24L01+ (CE, CSN) RF24 radio(9, 10); // Создаем массив для приема данных int data[2]; // подключение библиотеки DTH #include "DHT.h" // создание экземпляра объекта DHT DHT dhtD, DHT11); unsigned long millissendstatus=0; unsigned long millissenddata=0; unsigned long millisgetstatus=0;
Глава 12. Anduino и беспроводная связь 261 I/O - передаем, 1 - получаем int mode=0; void setup () { Serial.begin(9600); // Инициируем работу NRF24L01+ radio.begin(); // Указываем канал передачи данных (от 0 до 127), // 5 - значит, передача данных осуществляется на частоте 2,405 ГГц // (на одном канале может быть только 1 приемник и до б передатчиков) radio.setChannel(CHANNEL); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS) radio.setDataRate (RF24_1MBPS); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, // RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm) radio.setPALevel (RF24_PA_HIGH); // Открываем для передачи данных с идентификатором ID radio.openWritingPipe (ID); Serial.println("start radio"); // запуск DHT dht.beginO ; Serial.println("ready"); void loopO { // отправляем данные radio if(mode==0) { // отправка данных влажности и температуры if (millis () -millissenddata>PAUSE_SEND_DATA) { // получение данных с датчика int h = dht.readHumidity(); Serial.print("Humidity="); Serial.print(h); data[0] = HUMIDITY; data[l] = h; // отправляем данные из массива data radio.write(&data, sizeof(data)); delayA00); int t = dht.readTemperature(); Serial.print(" Temperature="); Serial.println(t); data[0] = TEMP; data[l] = t; // отправляем данные из массива data radio.write(&data, sizeof(data)); millissenddata=millis(); millissendstatus=millis();
262 Часть III. Практическое применение Arduino I/ отправка для ответа - статус сети if (millis () -millissendstatus>PAUSE_SEND_STATUS) { data[0] = STAT; datafl] = 1; Serial.printIn("send"); // отправляем данные из массива data radio.write(&data, sizeof(data)); delayA00); Serial.printIn("ok") ; millissendstatus=millis(); radio.openReadingPipeA, ID); radio.startListening(); mode=l; millisgetstatus=millis (); delayA00); // получаем данные radio else { if(radio.available()) { radio, read (&data,sizeof(data)); Serial.print("get data="); Serial.print(data[0]); Serial.print(" ") ; Serial.println(data[1]); mode=0; radio.stopListening(); delayA00); radio.openWritingPipe (ID); } if (millis () -millisgetstatus>TIME_WAIT_STATUS) mode=0; Serial.print("status - NO"); Serial.println(); radio.stopListening(); radio.openWritingPipe (ID); Электронный архив Полный вариант рассмотренного скетча находится в папке examplesKn^njOS сопровождающего книгу электронного архива (см. приложение 2). Вторая плата Arduino, получая по радиоканалу данные температуры и влажности, выводит их в последовательный порт. Если полученные данные — это запрос статуса, то отправляет ответ.
Глава 12. Arduino и беспроводная связь 263 Содержимое скетча для второй платы Arduino показано в листинге 12.6. // идентификатор канала idefine ID 0x357340 // номер канала fdefine CHANNEL 5 // нулевой байт idefine TEMP 56 idefine HUMIDITY 57 tdefine STAT 59 // Подключаем библиотеку для работы с шиной SPI iinclude <SPI.h> // Подключаем файл настроек из библиотеки RF24 tinclude <nRF24L01.h> // Подключаем библиотеку для работы с NRF24L01 iinclude <RF24.h> // Создаем объект radio для работы с библиотекой // RF24, указывая номера выводов NRF24L01+ (СЕ, CSN) RF24 radio (9, 10); // Создаем массив для приема данных int data[2]; void setup () { // Запуск последовательного порта Serial.begin(9600); Serial.begin(9600); // Инициируем работу NRF24L01+ radio.begin(); // Указываем канал передачи данных (от 0 до 127), // 5 - значит, передача данных осуществляется на частоте 2,405 ГГц // (на одном канале может быть только 1 приемник и до 6 передатчиков) radio.setChannel(CHANNEL); // Указываем скорость передачи данных (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS) radio.setDataRate (RF24_1MBPS); // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, // RF24_PA_LOW=-12dBm, RF24_PA_HIGH=~6dBm, RF24_PA_MAX=0dBm) radio.setPALevel (RF24_PA_HIGH); // Открываем для приема данных с идентификатором ID radio.openReadingPipe A, ID); Serial.println("start radio"); // Включаем приемник, начинаем прослушивать radio.startListening ();
264 Часть III. Практическое применение Arduino void loop () { // Если в буфере имеются принятые данные if(radio.available()) { // Читаем данные в массив data и указываем, сколько байтов читать radio.read(&data, sizeof(data)); // проверка dataO switch(data[0]) { case TEMP: // температура текущая Serial.print("Temperatura tek="); Serial.println(data[1]) ; break; case HUMIDITY: // влажность текущая Serial.print("Humidity tek="); Serial.println(data[l]); break; case STAT: // проверка статуса сети Serial.print("status="); Serial.println(data[1]); radio.stopListening (); //на отправку delayA50); radio.openWritingPipe (ID); // отправить radio.write(&data, sizeof(data)); //на получение radio.openReadingPipe (lf ID); // Включаем приемник, начинаем прослушивать radio.startListening (); break; default: break; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\12\_i2_06 сопровождающего книгу электронного архива (см. приложение 2). 12.4. Использование Arduino с аппаратурой радиоуправления Аппаратура радиоуправления используется для управления движущимися моделями и состоит из передатчика, который находится у пилота, и размещенных на модели приемника и исполнительных механизмов. Для управления исполнительными
Глава 12. Arduino и беспроводная связь 265 механизмами нередко задействуются платы Arduino, которые должны получать команды от приемника и обрабатывать их в соответствии с заложенными в их программу алгоритмами. По конструкции органов управления, на которые, собственно, и воздействуют пальцы пилота, передатчики делятся на джойстиковые (рис. 12.11) и пистолетного типа. В первых — которые и используются в основном для управления летающими моделями — установлены, как правило, два двухкоординатных джойстика. Рис. 12.11. Передатчик НК-Т6А Для управления движущимися моделями требуется выполнение одновременно нескольких функций. Поэтому передатчики радиоуправления делают многоканальными. Так, для авто- и судомоделей требуются два канала: управление направлением движения и оборотами двигателя. Для полноценного управления самолетом нужно не менее четырех, а вертолетом — пяти каналов. Для самолетов на два двухкоординатных джойстика выводятся функции управления рулем высоты, направления, элеронами и «газом» (оборотами) двигателя. Конкретная раскладка функций по джойстикам бывает двух типов: ? Mode 1 (рис. 12.12): слева — руль высоты (по вертикали) и руль направления (по горизонтали), справа — «газ» (по вертикали) и крен (по горизонтали); ? а также Mode 2 (рис. 12.13): слева— «газ» (по вертикали) и руль направления (по горизонтали), справа — руль высоты (по вертикали) и крен (по горизонтали). Есть еще типы раскладок Mode 3 и 4, но они мало распространены.
266 Часть III. Практическое применение Arduino вверх холостой ход Рис. 12.12. Раскладка Mode 1 холостой вверх ход Рис. 12.13. Раскладка Mode 2 12.4.1. Принципы формирования радиосигнала Для того чтобы излучаемый передатчиком радиосигнал мог переносить полезную информацию, он подвергается модуляции. То есть управляющий сигнал изменяет параметры несущей радиочастоты. На практике нашли применение управление амплитудой и частотой несущей, обозначаемые буквами AM (Amplitude Modulation, амплитудная модуляция) и FM (Frequency Modulation, частотная модуляция). В радиоуправлении используется только дискретная двухуровневая модуляция. В варианте AM несущая имеет либо максимальный, либо Нулевой уровень. В варианте FM излучается сигнал постоянной амплитуды либо с частотой F, либо с чуть смещенной частотой F + df. Сигнал FM-передатчика напоминает сумму сигналов двух АМ-передатчиков, работающих в противофазе на частотах F и F + df соответственно. Из этого можно понять, даже не углубляясь в тонкости обработки радиосигнала в приемнике, что в одинаковых помеховых условиях FM-сигнал имеет принципиально большую помехозащищенность, чем АМ-сигнал. АМ-аппаратура, как правило, дешевле, однако разница не очень велика. В настоящее время использование АМ-аппаратуры оправдано только для тех случаев, когда расстояние до модели относительно невелико. Как правило, это справедливо для автомоделей, судомоделей и комнатных авиамоделей. Вообще, летать с использованием АМ-аппаратуры можно лишь с большой опаской и вдали от промышленных центров. Модуляция позволяет наложить на излучаемую несущую полезную информацию. Однако в радиоуправлении используется только многоканальная передача информации. Для этого все каналы уплотняются в один посредством кодирования. Сейчас
Глава 12. Arduino и беспроводная связь 267 для этого используется только импульсно-фазовая модуляция, обозначаемая буквами PPM (Pulse Phase Modulation) и импульсно-кодовая модуляция, обозначаемая буквами PCM (Pulse Code Modulation). На рис. 12.14 приведен типовой РРМ-сигнал пятиканальной аппаратуры, имеющий фиксированную длину периода Т = 20 мс. Это означает, что информация о положениях ручек управления на передатчике попадает на модель 50 раз в секунду, что определяет быстродействие аппаратуры управления. Как правило, этого хватает, поскольку скорость реакции пилота на поведение модели намного меньше. Все каналы пронумерованы и передаются по порядку номеров. Значение сигнала в канале определяется величиной временного промежутка между первым и вторым импульсом — для первого канала, между вторым и третьим — для второго канала и т. д. Диапазон изменения величины временного промежутка при движении джойстика (ручки управления) из одного крайнего положения в другое определен от 1 до 2 мс. Значение 1,5 мс соответствует среднему (нейтральному) положению джойстика. Продолжительность межканального импульса составляет около 0,3 мс. Такая структура РРМ-сигнала является стандартной для всех производителей аппаратуры радиоуправления. „ 0.3 мс . 2 КАНАЛЫ 3 4 Т=20мс Рис. 12.14. Типовой РРМ-сигнал пятиканальной аппаратуры 12.4.2. Организация связи приемника с передатчиком Рассмотрим организацию связи приемника НК-Т6А (рис. 12.15), установленного на управляемой модели в нашем проекте, с передатчиком управляющего сигнала. Как можно видеть, число каналов управления у этого приемника — шесть. Для начала необходимо связать между собой передатчик и приемник в следующем порядке: 1. Установить в передатчик батарею. 2. Вставить шнур для кодирования (показан на рис. 12.15, вверху) в контакты ВАТ приемника.
268 Часть III. Практическое применение Arduino Рис. 12.15. Приемник НК-Т6А (внизу); шнур с разъемом для кодирования приемника (вверху) 3. Соединить батарею питания приемника с одним из портов канала — если свето- диоды на приемнике и передатчике вспыхивают одновременно, значит, приемник успешно включен. 4. Зажать (нажать и удерживать) кнопку поиска частоты на передатчике и включить питание — если светодиоды на приемнике не мигают, а просто горят, то связь установлена. 5. Отпустить кнопку на передатчике, отсоединить шнур на приемнике. 6. Установить сервомашинку в какой-нибудь из каналов и проверить работоспособность— при движении джойстиков на передатчике сервомашинка должна вращаться. Теперь можно подключить приемник к Arduino и разработать скетч для приема команд с передатчика. 12.4.3. Разработка скетча для приема платой Arduino команд передатчика Для считывания платой Arduino данных, поступающих с передатчика на приемник, подключаем сигнальные контакты приемника на выводы Dll, D10, D9, D8 платы Arduino. He забываем подать на приемник питание 5 В. Диапазон изменения величины временного промежутка при движении джойстика из одного крайнего положения в другое определен величиной от 1 до 2 мс. Для определения длительности сигнала, поступающего на входы, мы воспользуемся функцией puiseino. Напомним, что функция считывает длину сигнала на заданном порту (high или low). Например, если задано считывание high, функция puiseino ожидает, пока на заданном порту не появится high. Когда high получено, включается таймер, который
Гпава 12. Arduino и беспроводная связь " 269 будет остановлен, когда на порту входа/выхода появится low. Длину сигнала функция puiseino возвращает в микросекундах. Если же в течение заданного времени (тайм-аута) сигнал на порту зафиксирован не был, функция возвращает 0. Синтаксис функции puisein (): pulseln(pin, value) pulseln(pin, value, timeout) Параметры: ? pin — номер порта входа/выхода, на котором будет ожидаться сигнал; ? value — тип ожидаемого сигнала (high или low); ? timeout— время ожидания сигнала (тайм-аут) в микросекундах; по умолчанию — одна секунда. Возвращаемое значение: длина сигнала в микросекундах или 0, если сигнал не получен до истечения тайм-аута. Выбираем поочередно порт для 1, 2, 3 и 4-го каналов, параметр vaiue=HiGH, timeout=2 мс. Получаемое значение сигнала: от 1 до 2 мс. Данные, полученные с передатчика, выводим в последовательный порт (рис. 12.16). Как можно видеть, при перемещении джойстиков передатчика изменяется значение считываемого сигнала. Рис. 12.16. Вывод данных, полученных платой Arduino с передатчика, в монитор последовательного порта Содержимое скетча, обеспечивающего прием платой Arduino команд передатчика, приведено в листинге 12.7. // передатчик - НК-Т6А // приемник - // приемник :
270 Часть III. Практическое применение Arduino // Chi - Rudder (руль направления, рыскание, YAW) // Ch2 - Elevator (тангаж, PITCH) // Ch3 - Throttle (газ) // Ch4 - Aileron (элероны, ROLL) unsigned long ChlValue,Ch2Value,Ch3Value,Ch4Value; unsigned long lastl,Iast2,last3,last4; int pinChl=ll; int pinCh2=10; int pinCh3=9; int pinCh4=8; void setup() { Serial.begin(9600); Serial.println("Ready"); pinMode (pinChl, INPUT); // connect Rx channel 1 pinMode (pinCh2, INPUT); // connect Rx channel 2 pinMode (pinCh3, INPUT); // connect Rx channel 3 pinMode (pinCh4, INPUT); // connect Rx channel 4 lastl = pulseln (pinChl, HIGH); //read RC channel 1 Iast2 = pulseln (pinCh2, HIGH); //read RC channel 2 Iast3 = pulseln (pinCh3, HIGH); //read RC channel 3 Iast4 = pulseln (pinCh4, HIGH); //read RC channel 4 void loop(){ // ChlValue = pulseln (pinChl, HIGH, 20000); //read RC channel 1 if (ChlValue — 0) {ChlValue = lastl;} else {lastl = ChlValue;} Serial.print (" Chi: ");Serial.print (ChlValue); // Ch2Value = pulseln (pinCh2, HIGH, 20000); //read RC channel 2 if (Ch2Value = 0) {Ch2Value = Iast2;} else {Iast2 - Ch2Value;} Serial.print(и Ch2: ")/Serial.print (Ch2Value); // Ch3Value » pulseln (pinCh3, HIGH, 20000); //read RC channel 3 if (Ch3Value = 0) {Ch3Value = Iast3;} else {Iast3 = Ch3Value;} Serial.print(я Ch3: ")/Serial.print (Ch3Value); // Ch4Value = pulseln (pinCh4, HIGH, 20000); //read RC channel 4 if (Ch4Value = 0) {Ch4Value = Iast4;} else {Iast4 = Ch4Value;} Serial.print(lf Ch4: ");Serial.print (Ch4Value) ; Serial.printIn("");
Глава 12. Arduino и беспроводная связь 271 Электронный архив Полный вариант рассмотренного скетча находится в папке examples\12\__i2JO сопровождающего книгу электронного архива (см. приложение 2). 12.5. Arduino и Bluetooth Беспроводной интерфейс Bluetooth является одним из самых популярных интерфейсов, которые любители программирования Arduino выбирают для связи создаваемого ими устройства с мобильным приложением. Связь, как правило, осуществляется с помощью подключаемых к плате Arduino Bluetooth-модулей НС-05 или НС-06 — недорогих и широко распространенных. Bluetooth-модуль общается с платой Arduino по последовательному порту и работает в двух режимах: ? отправки/получения по Bluetooth данных, поступающих на него по последовательному порту; ? в режиме программирования модуля отправкой АТ-команд. Для входа в режим программирования необходимо подать на контакт KEY модуля (рис. 12.17) сигнал высокого уровня 3,3 В. На некоторых модулях контакт KEY отсутствует, вместо него имеется контакт EN. В этом случае для входа в режим программирования необходимо подать сигнал высокого уровня на контакт 34 (рис. 12.18). Рис. 12.17. Bluetooth-модуль НС-05 Рис. 12.18. Bluetooth-модуль НС-05 с контактом KEY без контакта KEY: подключение для входа в режим программирования Рассмотрим настройку модуля в режиме программирования отправкой АТ-команд по последовательному порту. Подключим модуль НС-05 к плате Arduino по схеме соединений, показанной на рис. 12.19. На контакт KEY модуля (или вывод 34-й пла-
272 Часть Hi Практическое применение Arduino ты) подадим 3,3 В. АТ-команды будем отправлять с монитора последовательного порта Arduino IDE. При программировании подключаем Arduino-библиотеку SoftwareSerial. Содержимое скетча показано в листинге 12.8. Скорость UART-модуля в режиме программирования 38 400 бод, но может и отличаться, в этом случае ее следует подобрать. Рис. 12.19. Монтажная схема подключения модуля НС-05 к плате Arduino для режима отправки АТ-команд #include <SoftwareSerial.h> // указываем пины гх и tx соответственно SoftwareSerial mySerialB, 3); void setup() { pinModeB,INPUT); pinModeC,OUTPUT); Serial.begin(9600); mySerial.beginC8400);
Глава 12. Arduino и беспроводная связь 273 Serial.println("start prg"); void loopO { if (mySerial.availableO ) { char с = mySerial.read(); // читаем из software-порта Serial.print(с); // пишем в hardware-порт } if (Serial.available()) { char с = Serial.read(); // читаем из hardware-порта mySerial.write(с); // пишем в software-порт Электронный архив Полный вариант рассмотренного скетча находится в папке examples\12\_12__08 сопровождающего книгу электронного архива (см. приложение 2). Загрузив этот скетч в плату, откроем монитор последовательного порта Arduino IDE и начнем отправлять АТ-команды (рис. 12.20). Вот список основных АТ-команд: П at — тестовая команда. Параметров нет. Ответ модуля: ок ¦ШАМЕ-МЕТЕО (yt-зноби +ROLE? (Г7опуиить роль) & U д р у!? / п Q г - у ч щ г ь гщозые ATvCLAbS=:793u (устанош-ггь CLASS Рис. 12.20. Отправка АТ-команд в модуль НС-05
274 Часть III. Практическое применение Arduino ? at+version? — получить версию прошивки модуля. Параметров нет. Ответ модуля:+VERSION: <Param> где <Param> — версия прошивки Bluetooth-модуля. ? at+reset — сброс настроек. Параметров нет. Ответ модуля: ок ? at+orgl — установка пользовательских настроек модуля. Параметров нет. Ответ модуля: ок ? AT+ADDR? — ПОЛуЧИТЬ адрес МОДУЛЯ. Параметров нет. Ответ модуля: +ADDR: <Param> где <Param> — адрес Bluetooth-модуля NAP: UAP : LAP. ? AT+NAME? ПОЛуЧИТЬ ИМЯ МОДуЛЯ. Параметров нет. Ответ МОДУЛЯ: +NAME:<Param> где <Param> — имя Bluetooth-модуля. ? AT+NAME=<Param> — установить новое имя модуля. Параметр: <Param> — имя Bluetooth-модуля. Ответ модуля: +NAME:<Param> ок (или fail) ? at+pswd? — получить пин-код доступа к Bluetooth-модулю. Параметров нет. Ответ модуля: + PSWD:<Param> где <Param> — пин-код. По умолчанию 1234. ? AT+PSWD=<Param> — установить код доступа к Bluetooth-модулю. Параметр: <Param> — код доступа к модулю. Ответ модуля: ок (или fail) ? AT+CLASS=<Param> — установить режим работы модуля Bluetooth-модуля. Параметр: <Рагаш> — класс. В документации модуля не приведены возможные значения этого параметра. По умолчанию он установлен в 0. Если предполагается использовать модуль в режиме master, значение не надо изменять. Если использовать модуль в режиме slave, при значении параметра, равном 0, он не-
Глава 12. Arduino и беспроводная связь видим для устройств с операционной системой Android. Для видимости необходимо установить значение параметра, равное 7936. Ответ модуля: ок П AT+CLASS? — получить класс модуля. Параметров нет. Ответ модуля: +CLASS:<Param> где <Param> — класс модуля. ? at+role? — получить режим работы модуля. Параметров нет. Ответ МОДУЛЯ: +ROLE:<Param> где <Param> — режим работы модуля Bluetooth-модуля: • о — slave. В этом режиме другой мастер может подключиться к модулю; • 1 — master. В этом режиме модуль может сам подключиться к какому-нибудь Bluetooth-устройству; • 2 — slave-loop. Модуль отправляет обратно все байты, которые ему прислали. AT+ROLE=<Param> — установить режим работы Bluetooth-модуля. Параметр: <Param> — режим работы Bluetooth-модуля: • о — slave; • 1 — master; • 2 — slave. Ответ модуля: ок ? AT+UART=<Parami>, <Param2>, <Param3> — установить модуль для последовательно- го порта. Параметры: • <Parami> — скорость обмена (9600, 19 200, 38 400, 57 600, 115 200); • <Param2> — СТОП-бит: D о — нет; D 1 — есть; <РагашЗ> — бит паритета: п о — нет; D 1 — есть. Ответ модуля: ок (или fail) ? at+uart? — получить параметры обмена модуля. Параметров нет.
276 Часть III. Практическое применение Arduino Ответ модуля: +UART: <Paraml>, <Param2>, <Param3> где: • <Parami> — скорость обмена (9600, 19 200, 38 400, 57 600, 115 200); • <Param2> — СТОП-бит; • <Param3> — бит паритета. ? AT+CMODE=<Param> — установить режим подключения Bluetooth-модуля. Параметр: <Param> — режим подключения Bluetooth-модуля: • о— модуль может подключаться только к определенному командой AT + BIND Bluetooth-устройству; • l — модуль может подключаться к любому Bluetooth-устройству; • 2 — режим slave-loop. Ответ модуля: ок ? AT+CMODE? — получить режим подключения модуля. Параметров нет. Ответ МОДУЛЯ: +CMODE:<Param> где <Param> — режим подключения Bluetooth-модуля: • о — модуль может подключаться только к определенному командой AT + BIND Bluetooth-устройству; • l — модуль может подключаться к любому Bluetooth-устройству; • 2 — режим slave-loop. ? AT+iNQ — запуск поиска Bluetooth-устройств. Параметров нет. Ответ модуля — список найденных устройств.. ? AT+BiND=<Param> — привязать Bluetooth-модуль к другому модулю. Параметр: <Param> — адрес авторизованного Bluetooth-модуля. Ответ модуля: ок (или fail) ? at+bind? — получить адрес устройства, привязанного к Bluetooth-модулю. Параметров нет. Ответ модуля: <Param> — адрес устройства, привязанного к Bluetooth-модулю. ? AT+LiNK=<Param> — соединиться с Bluetooth-устройством. Параметр: <Param> — адрес Bluetooth-устройства. Ответ модуля: ок (или fail)
Глава 12. Arduino и беспроводная связь 277 После программирования модуля отсоединим контакт KEY модуля (или вывод 34) от 3,3 В и попробуем подсоединиться к модулю со смартфона (или планшета) на операционной системе Android (рис. 12.21-12.23). Подключившись, можно организовать обмен данными между телефоном и платой Arduino. Рис. 12.21. Модуль НС-05 (устройство МЕТЕО найдено) ШяШШШШ >*?/Э '-^Й*'?'5''*Й.;^ч?С:i';'!-*!"!--, 1 Ы^У"ЖМг'%^ Рис. 12.22. Запрос на соединение с найденным устройством
278 Часть III. Практическое применение Arduino SM-T2.ll МЕТЕО Рис. 12.23. Подключение к модулю НС-05 (устройству МЕТЕО) с телефона Android состоялось
ГЛАВА 13 Arduino и Интернет вещей Интернет вещей (Internet of Things, IoT) — это широкая сеть объектов, связанных через Интернет и способных обмениваться данными. Интернет вещей предполагает оснащение каждого устройства, будь то пылесос, холодильник или стиральная машина, модулем подключения к Интернету с возможностью взаимодействия его с домашним компьютером или смартфоном домовладельца. В этой главе мы рассмотрим организацию доступа Arduino к сети Интернет с дальнейшей отправкой данных в известные облачные сервисы и получением их оттуда. 13.1. Подключение к Интернету с помощью платы расширения Ethernet shield Самый распространенный метод обеспечить доступ платы Arduino к сети Интернет— использование платы Ethernet shield (рис. 13.1). Ethernet shield— это плата расширения, которая устанавливается на плату Arduino сверху. Она дает ей возможность выступать в роли сетевого устройства и общаться по проводной сети с аналогичными устройствами, с обычными компьютерами, принтерами, сервисами в Интернете и прочими сетевыми ресурсами. Последняя версия платы Ethernet Shield Rev3 полностью совместима с Arduino Mega2560. Плата Ethernet shield основана на микросхеме Wiznet W5100, которая поддерживает как TCP-, так и UDP-протоколы. Одновременно открытыми могут быть до четырех подключений. Плата обладает стандартным Ethernet-портом для подключения к сети с помощью патч-корда витой пары и набором контактов для подключения к Arduino. Для общения между собой Ethernet shield и Arduino задействуют контакты 4-й и с 10-го по 13-й, поэтому их использование в других целях в присутствии платы расширения невозможно. Для программирования сетевого взаимодействия подключается библиотека Ethernet из стандартного дистрибутива. При использовании этой библиотеки необходимо указывать МАС-адрес платы (уникальный идентификатор любого сетевого устройства). В более новых версиях Ethernet-шилда МАС-адрес можно увидеть на наклей-
280 Часть III. Практическое применение Arduino ке на плате. Если такой наклейки нет, то просто введите любую похожую комбинацию, — главное, чтобы в вашей сети не было устройств с совпадающими МАС- адресами. На плате размещен слот для карты памяти формата microSD, которая может быть использована для хранения ресурсов, раздаваемых по сети. Для взаимодействия с такой картой следует подключить, например, библиотеку sdfatlib. Для отправки данных в облачные сервисы в примерах этого раздела мы воспользуемся веб-клиентом на основе платы Arduino с установленной на нее платой расширения Ethernet shield. Рис. 13.1. Плата Ethernet shield Rev3 13.1.1. Получение IP-адреса по DHCP Соединим Ethernet shield с платой Arduino и создадим простой пример получения ими IP-адреса по DHCP. Соединяется Ethernet shield с платой Arduino так же просто, как и любой другой шилд, — просто состыкуйте их вместе. Следует учесть, что установка других шилдов поверх Ethernet shield весьма затруднительна. Это связано с большими размерами имеющегося на плате Ethernet shield разъема RJ-45, служащего для подключения сетевого кабеля, поэтому, если вы хотите использовать совместно с Arduino еще и другие шилды, лучше их размещать между Arduino и Ethernet shield. Итак, подключим плату Arduino к USB-порту компьютера, a Ethernet shield подсоединим с помощью сетевого кабеля к маршрутизатору, имеющему выход в Интернет (рис. 13.2). Скетч, обеспечивающий получение IP-адреса по DHCP, представлен в листинге 13.1, а пример назначения статического IP-адреса — в листинге 13.2.
Глава 13. Arduino и Интернет вещей 281 Рис. 13.2. Подключение к плате Arduino платы расширения Ethernet shield Rev3 i // Получение IP-адреса по DHCP // МАС-адрес Ethernet shield (можно увидеть на наклейке на плате) или // произвольный уникальный в сети iinclude <Ethernet.h> If include <SPI.h> byte mac[] = {0x00, OxAA, OxBB, OxCC, OxDE, 0x02}; void setup () { // Open serial communications and wait for port to open: Serial.begin(9600); } // запуск Ethernet-соединения if (Ethernet.begin(mac) ==0) { Serial.println("Failed to configure Ethernet using DHCP"); for (;;) // печать в последовательный порт полученного по DHCP адреса Serial.print("My IP address: "); for (byte thisByte = 0; thisByte < 4; thisByte++) { Serial.print(Ethernet.locallPO [thisByte], DEC); Serial.print("."); } Serial.println(); void loop () {;} Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_13__01 сопровождающего книгу электронного архива (см. приложение 2).
282 Часть III. Практическое применение Arduino I/ Получение статического IP-адреса // МАС-адрес Ethernet shield (можно увидеть на наклейке на плате) или // произвольный уникальный в сети #include <Ethernet.h> #include <SPI.h> byte mac[] = {0x00, OxAA, OxBB, OxCC, OxDE, 0x02}; // IP-адрес, назначаемый Ethernet shield: byte ip[] = { 192, 168, 0, 111 }; // IP-адрес dns сервера: byte sdns[] = { 192, 168, 1, 1 }; // адрес шлюза: byte gateway[] = { 192, 168, 0, 1 }; // маска: byte subnet[] = { 255, 255, 255, 0 }; void setup() { Serial.begin(9600); // запуск Ethernet-соединения Ethernet.begin(mac, ip, sdns, gateway, subnet); delayA000); Serial.println(Ethernet.locallP()); } void loop() {;} Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_13_02 сопровождающего книгу электронного архива (см. приложение 2). 13.1.2. Отправка данных на сайт «Народный мониторинг» через Ethernet shield Получив доступ в Интернет, мы можем отправлять данные с платы Arduino в облачные сервисы. Рассмотрим пример отправки данных на сайт «Народный мониторинг». «Народный мониторинг» (http://www.narodmon.ru) — это проект по сбору и отображению на карте мира показаний температуры, атмосферного давления, влажности и т. п. практически в режиме реального времени по фактическому их состоянию (а не на основе прогнозов), получаемых от различных датчиков среды, установленных как на улице для публичного доступа, так и в помещении для приватного, а также от веб-камер для частного или публичного доступа. Передавать показания датчиков на сайт «Народный мониторинг» можно посредством протоколов TCP/UDP
Глава 13. Arduino и Интернет вещей 283 или HTTP GET/POST. Минимальный интервал передачи показаний датчика — 5 мин (если передавать чаще, то возможна блокировка). Чтобы стать участником проекта, необходимо зарегистрироваться. Для этого заходим на сайт http://www.narodmon.ru и выбираем пункт меню Вход | Стать участником проекта. В регистрационной форме (рис. 13.3) вводим адрес электронной почты, на который будут отправлены логин и пароль для входа в профиль. Рис. 13.3. Регистрация на сайте «Народный мониторинг» Подключим к плате Arduino с установленной на нее платой расширения Ethernet shield датчик температуры LM335 (рис. 13.4) и настроим передачу показаний этого датчика на сайт «Народный мониторинг» и отображение их на его карте мира. Рис. 13.4. Монтажная схема подключения датчика температуры LM335 к плате Arduino с установленной на ней платой Ethernet shield W5100
284 Часть III. Практическое применение Arduino Для добавления датчика на карту необходимо выполнить следующие действия: 1. Подключить устройство мониторинга (в нашем случае — плату Arduino с датчиком LM335) к источнику питания и к сети Интернет (через Ethernet shield). 2. Настроить передачу показаний на сайт «Народный мониторинг» с интервалом 5-15 мин (если чаще, то возможна блокировка). 3. Авторизоваться на сайте «Народный мониторинг», используя свой логин (e-mail или номер мобильного телефона) и пароль, полученные при регистрации. 4. В разделе сайта Мои Датчики добавить устройство, введя его уникальный алфавитно-цифровой код (МАС-адрес). Имейте при этом в виду, что добавление возможно только после успешной передачи показаний на сервер и при верно указанном МАС-адресе. 5. Выбрать тип данных для каждого из датчиков: температура, влажность, давление и пр. 6. Установить доступ к показаниям для каждого датчика: публичный (виден всем) или приватный (только вам). 7. Указать названия для устройства мониторинга и подключенных к нему датчиков. 8. Выполнить привязку устройства мониторинга к карте, указав полный адрес его размещения или геокоординаты и щелкнув на строке с адресом в графе УСТРОЙСТВО раздела сайта Мои Датчики (уточнить местоположение можно, щелкнув на карте на маркере своего устройства и переместив в нужное место появившееся всплывающее окно). Для создания скетча (листинг 13.3) мы возьмем за основу рекомендованный ресурсом student-proger.ru пример подключения к сервису «Народный мониторинг» и изменим его под свои требования (сетевые параметры) и датчики. #include <SPI.h> #include <Ethernet.h> byte mac[] = { 0x94, OxDE, 0x80, ОхЗА, 0x90, 0xC9 }; //МАС-адрес Arduino const unsigned long postinglnterval = 600000; // интервал между отправками // данных 10 мин // IP-адрес, назначаемый Ethernet shield: byte ip[] = { 192, 168, 0, 119 }; // IP адрес, dns сервера: byte sdns[] = { 192, 168, 1, 1 }; // адрес шлюза: byte gateway[] = { 192, 168, 0, 28 }; // маска: byte subnet[] = { 255, 255, 255, 0 };
Глава 13. Arduino и Интернет вещей 285 IPAddress server(94,19,113,221); // IP сервера //IPAddress server(91,122,49,168); // IP сервера EthernetClient client; unsigned long lastConnectionTime =0; // время последней передачи данных boolean lastConnected = false; // состояние подключения char replyBuffer[160]; void setup() { Serial.begin(9600); // Ethernet connection: Ethernet.begin(mac,ip,sdns,gateway,subnet); // секунда для инициализации Ethernet delayA000); // первое соединение через 15 с после запуска lastConnectionTime = millis()-postinglnterval+l5000; void loop() { // если не подключены и прошло определенное время, то делаем замер, // переподключаемся и отправляем данные if (!client.connected() && (millis () - lastConnectionTime > postinglnterval)) { // формирование HTTP-запроса memset(replyBuffer, 0, sizeof(replyBuffer)); strcpy(replyBuffer,"ID="); // Конвертируем МАС-адрес for (int k=0; k<6; k++) { int bl=*nac[k]/16; int b2=*nac[k]%16; char cl[2],c2[2]; if (bl>9) cl[0]=(char)(bl-10)+'Af; else cl[0] = (char)(bl) + '0'; if (b2>9) c2[0]=(char)(b2-10)+'A'; else c2[0] = (char)(b2) + f0f; cl[l]='\0'; c2[l]='\0f; strcat(replyBuffer,cl); strcat(replyBuffer,c2); } strcat(replyBuffer,"&"); strcat(replyBuffer,351C4BA0200003B"); strcat(replyBuffer,"=");
286 Часть III. Практическое применение Arduino char temp[3]; double tmpd=(analogRead(АО)*5.0/1024)П00-273.15; int tmpi=int (tmpd); itos(tmpi, temp); strcat(replyBuffer,temp); strcat(replyBuffer,f\0'); //отправляем запрос httpRequest(); } // храним последнее состояние подключения lastConnected = client.connected(); } // функция отправки запроса void httpRequest() { if (client.connect(server, 80)) { // send the HTTP POST request: client.println("POST http://narodmon.ru/post.php HTTP/1.0"); client.println("Host: narodmon.ru"); client.println("Content-Type: application/x-www-form-urlencoded"); client.print("Content-Length: "); client.println(len(replyBuffer)); client.println(); client.println(replyBuffer); client.println(); lastConnectionTime = millisO; } else { client.stop(); // размер данных int len(char *buf) { int i=0; do } while (buf[i]!='\0f); return i; // функция int to string void itos(int n, char bufp[3]) // char buf[3] = {'0\ f0\ f\0f};
Глава 13. Arduino и Интернет вещей 287 int i = 1; while (n > 0) { buf[i] = (n % 10)+48; i—; n /= 10; } for (i=0; i<3; bufp[i]=buf[i]; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i3\_i3_03 сопровождающего книгу электронного архива (см. приложение 2). Для передачи данных на сайт «Народный мониторинг» мы здесь используем резервный протокол передачи HTTP POST/GET на URL http://narodmon.ru/postphp. HTTP-заголовки для POST следующие: POST http://narocinon.ru/post.php HTTP/1.0\r\n Host: narodmon. ru\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: NN(кол-во байт в строке данных ниже)\г\п \г\п ID=MAC&macl=valuel&.. .&macN=valueN[&time=UnixTime] [&name=NAME] [&lat=LAT] [&lng=LNG] После загрузки в плату Arduino этот скетч запускает Ethernet-соединение, плата получает IP-адрес в Сети, один раз в 5 мин считываются данные с датчика температуры, формируется строка с данными для отправки на сервер сайта «Народный мониторинг» и данные отправляются с использованием протокола HTTP POST. Теперь авторизуемся на сайте, используя логин и пароль, пришедшие на электронную почту. Выбираем пункт меню Датчики | Мои Датчики | Добавить устройство и вводим МАС-адрес нашего устройства. Если данные уже были отправлены на сайт, устройство будет добавлено (рис. 13.5). Затем выбираем тип данных нашего датчика (температура), устанавливаем доступ к показаниям (приватный), указываем название устройства мониторинга и выполняем привязку его к карте, указав полный адрес и щелкнув на строке с адресом. Выбираем опцию Показать на карте (см. рис. 13.5) и в случае необходимости корректируем положение всплывающего окна (рис. 13.6). Через некоторое время мы можем посмотреть временной график изменения данных датчика на нашем устройстве. Для этого выбираем пункт меню Профиль | Мои Датчики и значок графика для выбранного датчика. Как можно видеть, на графике представлено изменение данных датчика во времени (рис. 13.7).
288 Часть III. Практическое применение Arduino " ' ' ' ' ¦¦ \ — — . _.__ — —; _ . _ .. „._ . ••*'¦-\ i Рис. 13.5. Добавление устройства на сайте «Народный мониторинг» Рис. 13.6. Всплывающее окно нашего устройства на карте сайта «Народный мониторинг»
Гпава 13. Arduino и Интернет вещей 289 Рис. 13.7. Временной график переданных на сайт «Народный мониторинг» показаний датчика 13.2. Подключение к Интернету с помощью платы расширения GSM/GPRS shield Плата расширения GSM/GPRS shield предоставляет возможность использовать в Arduino-проектах для удаленного приема и передачи данных сеть мобильной GSM- связи. GSM/GPRS shield позволяет осуществить это следующими способами: ? прием/отправка SMS; ? передача аудио (голос, CSD, DTMF); D связь по GPRS. Рассмотрим один из вариантов этого шилда — SIM900 Quad-Band GPRS shield на основе микросхемы GSM-модуля SIM900 (рис. 13.8). Основные характеристики GSM-модуля SIM900: ? четыре диапазона GSM: 850, 900, 1800, 1900 МГц; ? класс передачи данных GPRS multi-slot class 10/8; ? соответствие стандарту GSM фазы 2/2+; ? класс мощности 4 B Вт в диапазонах 850,900 МГц); ? класс мощности 1 A Вт в диапазонах 1800, 1900 МГц); ? управление АТ-командами (GSM 07.07, 07.05 и фирменные АТ-команды SIMCom); 0 аудиокодеки HR, FR, EFR, AMR, подавление эха; ? С8Бдо14,4Кб1Гг/с;
290 Часть III. Практическое применение Arduino Рис. 13.8. SIM900 Quad-Band GPRS shield ? РРР-стек; ? встроенный стек TCP/IP, UDP/IP; ? протоколы HTTP и FTP; ? протокол защищенных сокетов SSL; ? декодирование DTMF-tohob; ? e-Mail — формирование и отправка электронных писем посредством АТ-команд; ? SMS Autorun — исполнение АТ-команд, полученных по SMS от определенного абонента; ? 2,5 Mb user memory — встроенная память для пользовательских данных; ? MMS — формирование, дополнение пользовательскими файлами и отправка с помощью АТ-команд; ? AMR play — воспроизведение аудиофайлов в динамик или в сторону удаленного абонента; ? Jamming Detection — функция обнаружения глушения сигнала; ? FOTA — обновление прошивки модуля по беспроводному каналу; ? Easy Scan — получение информации об окружающих базовых станциях без подключения SIM-карты; ? PING — проверка доступности адреса в Интернете посредством обмена ICMP- пакетами. Особенности шилда SIM900 Quad-Band GPRS shield: ? совместимость с Arduino Mega; ? слот для карт SD (включение/отключение при помощи перемычки);
Fnaea 13. Arduino и Интернет вещей 291 О гнездо наушников «два в одном»; ? программное и аппаратное обеспечение последовательного порта — может общаться с Arduino через последовательный порт программного обеспечения (D2/D3) или последовательный порт (D0/D1); ? интерфейс FTDI; О слот батарейки для RTC; ? 10 цифровых входов/выходов GPIO; G два ШИМ-выхода; 0 интерфейс 12С. Рассматриваемый шилд имеет два способа включения: аппаратный (кратковременное нажатие кнопки PWRKEY) и программный (используется выход D7 Arduino). 13.2.1. Отправка и получение SMS-сообщений с помощью GSM/GPRS shield В этом примере мы каждые 30 мин будем отправлять на определенный телефонный номер SMS-сообщение с показаниями аналогового датчика температуры LM335, подсоединенного к выводу АО платы Arduino (рис. 13.9). Установим SIM-карту Рис. 13.9. Монтажная схема подключения к плате Arduino модуля GSM/GPRS shield и датчика LM335
292 Часть III. Практическое применение Arduino в слот GSM/GPRS shield, а сам GSM/GPRS shield — на плату Arduino. С помощью джамперов соединим контакты для работы через SoftwareSerial-эмуляцию. Содержимое скетча для отправки SMS показано в листинге 13.4. // подключение библиотеки SoftwareSerial #include <SoftwareSerial.h> // номер телефона для отправки sms (поменяйте на свой) #define PHONE "+79034461752" // Выводы для SoftwareSerial (у вас могут быть 7,8) SoftwareSerial Sim900SerialB, 3); const int lm335=A0; // для подключения LM335 unsigned long millisl; void setup() { Sim900SerialA9200); // активация последовательного соединения } void loop() { if (millis()-millisl>30*60*1000) // прошло 30 мин? { SendTextMessage(); // отправить sms millisl=millis (); // подпрограмма отправки sms void SendTextMessage() { // АТ-команда установки text mode Sim900Serial.print("AT+CMGF=l\r"); delayA00); // номер телефона получателя Sim900Serial.println("AT + CMGS = \""); Sim900Serial.println(PHONE); Sim900Serial.println("\""); delayA00); // сообщение - данные температуры double val = analogRead(lm335); // чтение double voltage = val*5.0/1024; // перевод в вольты double temp = voltage*100 - 273.15; // в градусы Цельсия Sim900Serial.println(temp); delayA00); // ASCII код ctrl+z - окончание передачи Sim900Serial.println((charJ6);
Гпава 13. Arduino и Интернет вещей 293 delayA00); Sim900Serial. println (); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_i3_04 сопровождающего книгу электронного архива (см. приложение 2). Загружаем этот скетч в плату Arduino, проверяем его работу и, если все в порядке, изменяем скетч таким образом, чтобы Arduino отправляла SMS-сообщение с данными температуры только при получении приходящего сообщения с текстом "temp" (ЛИСТИНГ 13.5). tinclude <SoftwareSerial.h> SoftwareSerial Sim900Serial B, 3) ; String currStr = ""; // String phone = ""; // // True, если текущая строка является sms-сообщением boolean isStringMessage = false; void setup () { Serial.beginA9200); Sim900Serial.beginA9200); // Настраиваем прием сообщений с других устройств Sim900Serial.print("AT+CMGF=l\r"); delayC00); Sim900Serial.print("AT+IFC=1, l\r"); delayC00); Sim900Serial. print ("AT+CPBS=\ffSM\if\r") ; delayC00); Sim900Serial.print("AT+CNMI=l/2f2/lf0\r"); delayE00); void loop () { if (!Sim900Serial.available()) return; char currSymb = Sim900Serial.read(); if (»\ri == currSynib) { if (isStringMessage) // текущая строка - sms-сообщение, { if (!currStr.compareTo("temp")) // текст sms - temp
294 . Часть III. Практическое применение Arduino // отправить sms на приходящий номер Sim900Serial.print("AT+CMGF=l\r"); delayA00); Sim900Serial.print("AT + CMGS = \""); Sim900Serial.print(phone); Siin900Serial.println("\"ff) ; delayA00); double val - analogRead(AO); // чтение double voltage = val*5.0/1024; // перевод в вольты double temp = voltage*100 - 273.15; // в градусы Цельсия Serial.printIn(temp); Sim900Serial.println(temp); delayA00); Sim900Serial.println((charJ6); delayA00); Sim900Serial.println(); } Serial.println(currStr); isStringMessage = false; } else { if (currStr.startsWith("+CMTff)) { Serial.println(currStr); // выделить из сообщения номер телефона phone^currStr.substringG,19); Serial.println(phone); //если текущая строка начинается с "+СМТ", //то следующая строка является сообщением isStringMessage = true; currStr = ""; } else if (f\nf != currSymb) { currStr += String(currSymb); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_13_05 сопровождающего книгу электронного архива (см. приложение 2).
Глава 13. Arduino и Интернет вещей 295 13.2.2. Отправка данных на сайт «Народный мониторинг» через GSM/GPRS shield В этом примере для отправки данных на сайт «Народный мониторинг» мы воспользуемся возможностями платы расширения GSM/GPRS shield. Итак, устанавливаем на плату Arduino GSM/GPRS shield и подключаем к ней датчик— все, как показано на рис. 13.9. В режиме отправки/получения данных GPRS модуль SIM900 потребляет ток до 2 А, поэтому ему понадобится внешнее питание. Для отправки HTTP-данных по GPRS-соединению необходимо выполнить отправку АТ-команд в следующей последовательности: 1. Первой отправляется команда at — ответ должен быть ок. 2. at+sapbr=i, 1 — установка GPRS-связи. 3. at+sapbr=3, I, "contype", "gprs" — настройка типа подключения: GPRS. 4. AT+SAPBR=3,1, "APN", "internet.beeline.ru" — настройка APN (в нашем СЛу- чае — для оператора «Билайн»). 5. AT+HTTPiNiT — инициализировать HTTP. 6. at+httppara="cid", l — carrier ГО для использования. 7. AT+HTTPPARA="URL", "http://narodmon.ru/post.php" — Собственно URL. 8. Строка get данных. 9. at+httpaction=o — данные методом GET. 10. Дождаться ответа. 11. AT+HTTPREAD ПОЛуЧИТЬ ОТВвТ. 12. at+httpterm — остановить HTTP. Содержимое скетча отправки данных представлено в листинге 13.6. tdefine INTERVALSEND 60000 tdefine LM335 АО iinclude <SoftwareSerial. h> SoftwareSerial GPRS G, 8) ; int onModulePin= 9; char aux_str [150]; char aux; char data [512]; int data_size; uint8_t answer=0; unsigned long millissend=O;
296 Часть III. Практическое применение Arduino char apn[]="internet.beeline.ru"; char url[150]; String surl="http://narodmon.ru/post.php/?"; void setup() { GPRS.beginA9200); // скорость для GPRS Serial.begin(9600); Serial.printIn("Starting..."); pinMode(onModulePin,OUTPUT); power_on(); delayC000); // точка доступа APN sendATcoramand ("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", "OK", 2000) ; snprintf (aux_str, sizeof (aux_str), "AT+SAPBR=3, 1, \"APN\", \"%s\ff", apn) ; sendATcoramand(aux_strf "OK", 2000); while (sendATcoramand("AT+SAPBR=1,1", "OK", 2000) == 0) { delayB000); } delayA000); void loopO { // отправка раз в 10 мин if (millis() -millissend>INTERVALSEND ) { // Initializes HTTP service answer = sendATcoramand("AT+HTTPINIT", "OK", 10000); if (answer = 1) { // Sets CID parameter answer = sendATcoramand("AT+HTTPPARA=\"CID\",1", "OK", 5000); if (answer = 1) {// Sets url double val = analogRead(LM335); // чтение показаний LM335 double voltage = val*5.0/1024; // перевод в вольты double temp = voltage*100 - 273.15; // в градусы Цельсия String surll=surl+"#A0:F3:Cl:70:AA:94\n#013950005243291#"+String(temp)+"\n##"; ' surll.toCharArray(url,surll.length()+1); snprintf(aux_str, sizeof(aux_str), "AT+HTTPPARA=\"URL\",\"%s\"", url) ; answer = sendATcoramand(aux_str, "OK", 5000); if (answer = 1) {// Starts GET action answer = sendATcoramand("AT+HTTPACTION=0", "+HTTPACTION:0,200", 10000);
Глава 13. Arduino и Интернет вещей 297 if (answer = 1) { sprintf(aux_str, "AT+HTTPREAD"); sendATcoramand(aux_str, "OK", 5000); } else { Serial.println("Error getting the url"); } } else { Serial.println("Error setting the url"); } } else { Serial.println("Error setting the CID"); } } else { Serial.println("Error initializating"); } sendATcoramanci ("AT+HTTPTERM", "OK", 5000); millissend=millis(); // отправка АТ-команд int8_t sendATcoramand(char* ATcommand, char* expected_answer, unsigned int timeout) { uint8_t x=0, answer=0; char response[150]; unsigned long previous; memset(response, 'NO1, 150); // Initialize the string delayA00); while ( GPRS.available () > 0) GPRS.readO; // Clean the input buffer GPRS.printIn(ATcoramand); // Отправка АТ-команды x = 0; previous = raillisO; // this loop waits for the answer do{ if(GPRS.available() != 0) { // if there are data in the UART input buffer, reads it and checks for the asnwer response [x] = GPRS.readO;
298 Часть III. Практическое применение Ardu'm // check if the desired answer is in the response of the module if (strstr(response, expected_answer) != NULL) { answer = 1; // время ожидания ответа while((answer == 0) && ((millisO - previous) < timeout)); Serial.println(response); return answer; } // программное включение питания void power__on() { uint8_t answer=0; pinMode(onModulePin,OUTPUT); // checks if the module is started digitalWrite(onModulePin,LOW); delayA000); digitalWrite(onModulePin,HIGH); delayB000); digitalWrite(onModulePin,LOW); delayC000); answer = sendATcommand(ffAT", "OK", 2000); if (answer = 0) { digitalWrite(onModulePin,LOW); delayA000); digitalWrite(onModulePin,HIGH); delayB000); digitalWrite(onModulePin,LOW); delayC000); digitalWrite(onModulePin,HIGH); delayC000); digitalWrite(onModulePin,LOW);*/ // время ожидания ответа while(answer — 0) { // Send AT every two seconds and wait for the answer answer = sendATcommand("AT", "OK", 2000);
Глава 13. Arduino и Интернет вещей 299 Электронный архив Полный вариант рассмотренного скетча находится в папке examplesU3^13^06 сопровождающего книгу электронного архива (см. приложение 2). Загружаем скетч в плату Arduino и проверяем отправку данных на сайт. После отправки данных можно добавить новое устройство в список устройств своего профиля на сайте «Народный мониторинг» (рис. 13.10). Рис. 13.10. Добавление нового устройства на сайте «Народный мониторинг» 13.3. Протокол MQTT В предыдущих разделах этой главы мы использовали для отправки/получения данных протокол HTTP. Рассмотрим еще один протокол взаимодействия устройств IoT — MQTT (Message Queue Telemetry Transport). Этот протокол обладает рядом преимуществ по отношению к другим протоколам: ? низкое потребление трафика; ? соединение между клиентом и сервером всегда открыто; ? не нагружает интернет-канал; П отсутствие задержек в передаче данных; П удобная система подписок на темы (топики); Все это дает возможности мониторинга и управления в режиме реального времени. Обмен сообщениями в протоколе MQTT осуществляется между клиентом (client), который может быть издателем (publisher) или подписчиком (subscriber) сообщений, и брокером (broker) сообщений (например, Mosquitto MQTT). Издатель и подписчик не передают друг другу сообщения напрямую, не устанавливают прямой
300 Часть III. Практическое применение Arduino контакт и могут не знать о существовании друг друга. Издатель отправляет данные на брокер MQTT, указывая в сообщении определенную тему — топик (topic). Подписчики могут получать разные данные от множества издателей в зависимости от подписки на соответствующие топики. Топики представляют собой символы с кодировкой UTF-8. Иерархическая структура топиков имеет формат «дерева», что упрощает их организацию и доступ к данным. Топики состоят из одного или нескольких уровней, которые разделены между собой символом «/»: /home/living/living-roonu/tenperature Устройства MQTT используют определенные типы сообщений для взаимодействия с брокером — далее представлены основные: ? connect — установить соединение с брокером; ? Disconnect — разорвать соединение с брокером; ? Publish — опубликовать данные в топик на брокере; ? subscribe — подписаться на топик на брокере; ? unsubscribe — отписаться от топика. Схема взаимодействия между подписчиком, издателем и брокером показана на рис. 13.11. Издатель -1. подключена JL. подтверждение подключения 3. публикация Брокер •1. подключение- 2. подтверждение подключения 3. подписка- %. публикация- Подписчик Рис. 13.11. Схема взаимодействия по протоколу MQTT Однако протокол MQTT требует наличия своего собственного сервера брокера. Тут есть два выхода: либо создавать свой сервер, либо использовать сторонние сервисы — например, удобный сервис www.cloudmqtt.com, у которого есть бесплатный тарифный план (Cute Cat). После регистрации на этом сервисе, выбора тарифного плана и местоположения сервера (EU или USA) создается устройство, в котором можно посмотреть ваши данные: адрес сервера, имя и пароль пользователя, порты подключения и ключ API key (рис. 13.12). 13.3.1. Отправка данных по протоколу MQTT Настроим отправку брокеру данных температуры и относительной влажности воздуха с датчика DHT11. Подключение платы Arduino к сети Интернет реализуем через Ethernet shield (см. разд. 13.2). Монтажная схема подключения для отправки данных с датчика DHT11 брокеру MQTT показана на рис. 13.13.
Fnaea 13. Arduino и Интернет вещей 301 о -л4 о=- г-, с? Рис. 13.12. Данные для взаимодействия с брокером MQTT на сервисе www.cloudmqttcom Рис. 13.13. Схема подключений для отправки данных с датчика DHT11 брокеру MQTT
302 Часть III. Практическое применение Articling Содержимое скетча показано в листинге 13.7. При написании скетча мы воспользуемся библиотекой PubSubClient. С периодичностью 10 с получаем данные с датчика DHT11 И отправляем на сервер В темы meteo/humidity И /meteo/temperature. Электронный архив Библиотека PubSubClient размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Замените в скетче переменные ip, sdns, gateway, subnet на данные для своей сети. Если ваша плата получает IP-адрес по DHCP, обратитесь к листингу 13.1. // Получение статического IP-адреса // МАС-адрес Ethernet Shield (можно увидеть на наклейке на плате) или // произвольный, но уникальный в сети #include <Ethernet.h> #include <SPI.h> byte mac[] = {0x00, OxAA, OxBB, OxCC, OxDE, 0x02}; // IP-адрес, назначаемый Ethernet Shield: byte ip[] = { 192, 168, 0, 111 }; // IP-адрес, dns сервера: byte sdns[] = { 192, 168, 1, 1 }; // адрес шлюза: byte gateway[] = { 192, 168, 0, 28 }; // маска: byte subnet[] = { 255, 255, 255, 0 }; EthernetClient ethclient; #include <PubSubClient.h> // данные mqtt #define mqtt_server lfm24. cloudmqtt. com" tdefine mqtt_port 18532 #define mqtt_user "cbadnzof" tdefine mqtt_pass "j63Z6DzKgogS" // создание pubsub клиента PubSubClient client; // список тем отправки tdefine topich "/meteo/humidity" tdefine topict "/meteo/temperature" tinclude <SimpleDHT.h> SimpleDHTll dhtll(8); byte temperature = 0; byte humidity =0;
Глава 13. Arduino и Интернет вещей 303 II периодичность отправки unsigned long millist=0; void setup () { Serial.begin(9600) ; // запуск Ethernet-соединения Ethemet.begin(mac, ip, sdns, gateway, subnet); delayA000); Serial.println(Ethernet.locallP()); client.setClient(ethclient); client.setServer(mqtt_server, mqttjport); reconnect(); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); if (millis()-millist>10000) { dht11.read(&temperature, &humidityf NULL); Serial.print((int)temperature); Serial.print(" *C, "); Serial.print ((int)humidity); Serial.println(" Hfl); publishData ((int) temperature, (int)humidity); // millist=millis(); // переподключение к mqtt void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection...$"); // Attempt to connect if (client.connect("Arduino+WiFi", mqtt_user, mqtt_pass)) { Serial.println("Connected to MQTT server$"); } else { Serial.println("Could not connect to MQTT server"); Serial.println(" try again in 5 seconds$"); // Wait 5 seconds before retrying delayE000);
304 Часть III. Практическое применение Arduino I/ отправка данных в темы брокера void publishData(int t, int h) { client.publish(topich, String(h).c_str(), true); delayE00); client.publish(topict, String(t).c_str(), true); delayE00); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_13_07 сопровождающего книгу электронного архива (см. приложение 2). Загрузив скетч в плату Arduino, заходим в профиль созданного нами устройства на сайте www.doudmqtt.com и в пункте WEBSOCKET UI видим приходящие в темы данные (рис. 13.14). ВШ^Ш^^Ш^^^^^}^^&Щ0Щ Рис. 13.14. Получение данных на брокере 13.3.2. Получение данных по протоколу MQTT В разд. 13.3.1 мы рассмотрели отправку показаний температуры и относительной влажности с датчика DHT11 на брокер MQTT, расположенный по адресу www.cloudmqtt.com. Рассмотрим теперь получение данных по протоколу MQTT для управления исполнительными устройствами, подключенными к модулю Relay shield. Монтажная схема подключения показана на рис. 13.15. Отправить необходимые команды для управления исполнительными устройствами можно из вашего профиля на сайте www.cloudmqtt.com, выбрав пункт меню WEBSOCKET UI и темы: meteo/relayl и /meteo/relay2 (рис. 13.16).
Глава 13. Arduino и Интернет вещей 305 Рис. 13.15. Монтажная схема подключения для управления исполнительными устройствами по протоколу MQTT Рис. 13.16. Отправка команд управления из профиля cloudmqtt.com Чтобы получать команды управления от сервера (брокера), наше устройство при подключении к брокеру должно подписаться на эти темы: client.subscribe("/meteo/relayl"); client.subscribe("/meteo/relay2");
306 Часть III. Практическое применение Arduino Для обработки сообщений от брокера необходимо назначить функцию обратного вызова callback, которая формирует строку команд управления, отправляемую по последовательному порту на Arduino: void callback(char* topic, byte* payload, unsigned int length) { String ss(topic); int value=0; Serial.print(topic); Serial.print("=")/ for (int i = 0; i < length; i++) { char receivedChar = (char)payload[i]; if (receivedChar = '0') {Serial.print(");value=0;} if (receivedChar = flf) {Serial.print(");value=l;} } if(ss.indexOf("/meteo/relayl")!=-l) { digitalWrite(pinRelayl,value);Serial.print(" relayl- ok"); } else if(ss.indexOf(f7meteo/relay2")!=-l) { digitalWrite(pinRelay2,value);Serial.print(" relay2- ok"); } Serial.println(); Рис. 13.17. Получение команд от брокера
Глава 13. Arduino и Интернет вещей 307 Электронный архив Полный вариант рассмотренного скетча находится в папке examples\13\_13__08 сопровождающего книгу электронного архива (см. приложение 2). Загрузив скетч в плату Arduino, открываем монитор последовательного порта и при отправке команд на сайте www.cloudmqtt.com в темы /meteo/relayl и /meteo/relay2 видим получение сообщений от брокера и установку новых значений для реле (рис. 13.17). 13.3.3. Android-приложение loT MQTT Dashboard Более удобный вариант обмена данными по протоколу MQTT— использование Android-приложения IoT MQTT Dashboard, которое можно задействовать в качестве пульта, а также для отображения информации, например, с датчиков домашней метеостанции. Приложение представляет собой готовый MQTT-клиент с небольшим количеством очень удобных виджетов. Итак, скачиваем это приложение на смартфон. Сначала создаем новое соединение и прописываем в нем данные из нашего профиля cloudmqtt (рис. 13.18). my home meieo ; m24,c;ioudrnqti com ;. ] 18532 :' Рис. 13.18. Создание соединения Выбираем созданное соединение и на вкладке PUBLISH создаем виджеты (switch) для отправки данных в темы /meteo/relayl и /meteo/relay2 (рис. 13.19). Вкя а б Рис. 13.19. Создание виджетов для отправки сообщений в темы
308 Часть III. Практическое применение Arduino Android-приложение IoT MQTT Dashboard позволяет также настроить отображение данных, которые отправляются брокеру. Для этого на вкладке SUBSCRIBE надо создать виджеты для отображения температуры и влажности и подписаться на топики /meteo/humidity, /meteo/temperature (рис. 13.20). В результате на экране смартфона мы получим табло с отображением этих данных (рис. 13.21) Рис. 13.20. Создание виджетов для получения данных температуры и влажности из соответствующих топиков Рис. 13.21. Отображение данных температуры и влажности на экране смартфона 13.4. Плата Arduino MKR WiFi 1010 для проектов IoT Плата Arduino MKR WiFi 1010 предназначена, как утверждают ее разработчики, для тех, кому нужно практичное, компактное и недорогое решение, сочетающее в себе функционал Wi-Fi и невысокие требования к знаниям в сетевых технологиях. Она основана на однокристальной системе (SoC) Atmel ATSAMD21G18 с вычислительном ядром ARM CortexMO, которая является частью семейства SmartConnect беспроводных устройств Atmel, созданных специально для проектов в области Интернета вещей. Чип ATSAMW25 состоит из трех главных блоков: ? 32-битного ARM-микроконтроллера ATSAMD21G18 с низким энергопотреблением; ? модуля беспроводной связи NINA-W1Q от компании u-blox со встроенным чипом ESP32. Модуль обеспечивает беспроводной обмен данными в диапазоне 2,4 ГГц по протоколам Wi-Fi и Bluetooth; ? крипточипа ЕСС508 для защиты передаваемых данных. Плата включает в себя схему зарядки Li-Po, которая позволяет Arduino MKR WiFi 1010 работать от батареи или от внешнего источника 5 В, заряжая батарею Li-Po во время работы от внешнего источника питания. Переключение с одного источника на другой осуществляется автоматически. Модуль Wi-Fi платы MKR WiFi 1010 поддерживает сертификат SHA-256.
Глава 13. Arduino и Интернет вещей 309 Хорошая 32-битная вычислительная мощность, богатый набор интерфейсов ввода/вывода, маломощный Wi-Fi с крипточипом для безопасной связи и простота использования программного обеспечения Arduino IDE для разработки кода и программирования— все эти функции делают плату MKR WiFi 1010 предпочтительным выбором для новых проектов IoT с батарейным питанием в компактном форм- факторе. Рассмотрим процесс программирования этой платы. В среде программирования Arduino IDE необходимо установить поддержку для Arduino SAMD C2bit ARM Cortex-M0+). Для этого заходим в Менеджер плат (Инструменты | Плата | Менеджер плат), находим Arduino SAMD и нажимаем на кнопку Установка (рис. 13.22 и 13.23). Рис. 13.22. Находим Arduino SAMD в Менеджере плат и нажимаем на кнопку Установка После этого в меню Инструменты Arduino IDE появится плата Arduino MKR 1000 (рис. 13.24). Примечание Как можно видеть на рис. 13.23, установка поддержки для Arduino SAMD в Arduino IDE обеспечивает поддержку всех плат Arduino MKR. Поэтому не стоит удивляться, что в меню Инструменты Arduino IDE все они определяются как Arduino MKR1000. Одной из основных особенностей MKR WiFi 1010 является возможность доступа к сети Wi-Fi. Для этого необходимо установить библиотеку WiFi 101. Установим ее с помощью Менеджера библиотек, выполнив команду меню Скетч | Подключить библиотеку | Управление библиотеками,— находим библиотеку WiFi 101 и нажимаем на кнопку Установка (рис. 13.25). Теперь загрузим в плату Arduino MKR WiFi 1010 пример из библиотеки ScanNetworks (WiFilOl | ScanNetworks) для сканирования точек доступа Wi-Fi (рис. 13.26).
310 Часть III. Практическое применение Arduino Рис. 13.23. Установка поддержки для Arduino SAMD в Arduino IDE Рис. 13.24. Выбор платы Arduino MKR1000
Глава 13. Arduino и Интернет вещей 311 v 1С: ^^ ' * ... .% .. Рис. 13.25. Установка библиотеки WiFM01 Рис. 13.26. Сканирование точек доступа W-iFi
312 Часть Ш. Практическое применение Arduino А затем подключимся к точке доступа Wi-Fi, загрузив пример из библиотеки ConnectWithWPA (WiFilOl | ConnectWithWPA) и установив в скетче значения SSID и пароль (рис. 13.27). Рис. 13.27. Пример подключения к точке доступа Wi-Fi 13.5. Отправка данных в облако Arduino loT Cloud и получение их оттуда В феврале 2019 г. компания Arduino объявила, что бета-версия их собственного облачного сервиса Arduino IoT становится общедоступной. С запуском Arduino IoT Cloud компания Arduino предоставляет миллионам своих пользователей полный комплексный подход к Интернету вещей, который включает аппаратное обеспечение, встроенное ПО, облачные сервисы. Arduino IoT Cloud — это способ Arduino демократизировать разработку IoT, позволяющий каждому легко создавать приложения для Интернета вещей. Это дает возможность устройствам на основе IoT обмениваться данными между собой и облаком, где может выполняться дальнейшая их обработка, и использовать эти данные для решения конкретных проблем. Платформа позволяет общаться через множество протоколов, в том числе HTTP REST API, MQTT, инструменты командной строки, Javascript и веб-сокеты. Arduino IoT Cloud имеет также инструменты, которые позволяют автоматически генерировать эскиз/код для вашего устройства, что по-
Глава 13. Arduino и Интернет вещей 313 могает сократить время разработки с часов до минут. Поддерживая типичный открытый исходный код и другие клоны Arduino, Arduino IoT Cloud позволяет подключать к платформе различные устройства, в том числе и платы на основе Linux. Рассмотрим пример создания проекта, взаимодействующего с облаком IoT Arduino в части отправки и получения данных, для чего соберем схему, показанную на рис. 13.28. Но прежде всего в Arduino IoT Cloud необходимо зарегистрироваться, перейдя в браузере по адресу: https://create.arduino.cc/io/ (рис. 13.29). 11 Рис. 13.28. Монтажная схема для взаимодействия с Arduino IoT Cloud я Рис. 13.29. Страница регистрации в сервисе Arduino IoT Cloud
314 . Часть III. Практическое применение Arduino Зарегистрировавшись и войдя в профиль, на следующей странице мы нажимаем кнопку Add new thing, которая позволяет нам настроить новое устройство IoT (новую «вещь») на облачной платформе, присвоив ей имя и выбрав используемую нами плату (рис. 13.30). При этом потребуется сначала сконфигурировать плату, выполнив процедуру начала работы. Рис. 13.30. Добавление нового устройства Затем добавляем свойство «вещи» (рис. 13.31). В нашем случае — это значение потенциометра. Рис. 13.31. Добавление свойства «вещи» (для потенциометра)
Глава 13. Arduino и Интернет вещей 315 Рис. 13.32. Добавление переключателя ON/OFF для отправки данных из облака на плату Для управления светодиодом с веб-страницы (отправки данных из облака на плату) создадим элемент — переключатель ON/OFF (рис. 13.32). Теперь переходим в Web editor для создания скетча — шаблон скетча там уже создан. Добавляем данные для подключения к точке доступа Wi-Fi, получение данных потенциометра и обработку данных из облака при использовании переключателя. Здесь же мы можем загрузить скетч в плату или сохранить на компьютере. Содержимое скетча показано в листинге 13.8. 1. // Подключение библиотек iinclude <WiFil01.h> Itinclude "thingProperties. h" // служебные переменные unsigned long lastConnectionTime = 0; const unsigned long postinglnterval = 2000; // статус светодиода int led_status=2; void setup () { // Открытие последовательного порта Serial.begin(9600); delayA500);
316 Часть III. Практическое применение Arduino I/ пин подключения светодиода pinModeF,OUTPUT); // инициализировать свойства initProperties(); initProperties(); // подключение к облаку Arduino IoT Cloud ArduinoCloud.begin(ArduinoIoTPreferredConnection); // setDebugMessageLevelB); ArduinoCloud.printDebuglnfо(); void loop() { ArduinoCloud.update(); // отправка по интервалу if (millisO - lastConnectionTime > postinglnterval) { Pot=map(analogRead(AO),0,1023,0,10000) ; Serial.println(Pot); lastConnectionTime=mLllis (); } // изменение состояния светодиода if (led_status=l) { digitalWriteF,HIGH); } else if(led_status==0) { digitalWriteF,LOW); void onLedChange() { if (led == true) led_status =1; else led_status = 0; Электронный архив Полный вариант рассмотренного скетча находится в папке examplesM3^13^09 сопровождающего книгу электронного архива (см. приложение 2). В профиле созданной «вещи» на вкладке Dashboard мы можем видеть наши вид- жеты с данными потенциометра и переключателем для изменения состояния светодиода (рис. 13.33).
Глава 13. Arduino и Интернет вещей 317 Рис. 13.33. Веб-страница созданной «вещи»
ГЛАВА 14 RFID-идентификация 14.1. Считыватель RFID RC522 RFED— это технология автоматической бесконтактной идентификации объектов при помощи радиочастотного канала связи. Любая RFID-система состоит из: ? RFID-метки; ? считывателя информации (RFID-ридера); ? микроконтроллера или компьютера для дальнейшей обработки информации. Идентификация объектов производится по уникальному цифровому коду, который считывается из памяти электронной метки, прикрепленной к объекту идентификации. Считыватель содержит в своем составе передатчик и антенну и посылает в эфир электромагнитные сигналы определенной частоты. RFID-метки «отвечают» на запрос считывателя собственным сигналом, который содержит информацию об идентификационном номере метки и данные об объекте, им оснащенной. Этот сигнал улавливается антенной считывателя, информация расшифровывается и передается для дальнейшей обработки. Считыватель RC522 представляет собой RFID-модуль, работающий на частоте 13,56 МГц с SPI-интерфейсом. В комплекте к модулю идут две RFID-метки: в виде карты и брелока. В RFID-метках, работающих на указанной частоте, реализована криптографическая защита, что уберегает от копирования и подделки. Монтажная схема подключения RFID-модуля RC522 к плате Arduino показана на рис. 14.1. Обратите внимание, что питание модуля осуществляется напряжением 3,3 В! Для взаимодействия Arduino с модулем RC522 мы применим Arduino-библиотеку MFRC522. Электронный архив Библиотека MFRC522 размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
Глава 14. RFlD-идентификация 319 Рис. 14.1. Монтажная схема подключения RF ID-модул я RC522 к плате Arduino Установим библиотеку и напишем скетч вывода UID-метки и ее типа в последовательный порт (листинг 14.1). // Подключение библиотек tinclude <SPI.h> finclude <MFRC522.h> // константы подключения контактов SS и RST tdefine RST_PIN 9 fdefine SS_PIN 10 // Инициализация MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN) ; // Create MFRC522 instance. void setup () { Serial.begin(9600); // инициализация последовательного порта SPI.beginO; // инициализация SPI mfrc522.PCD Init(); // инициализация MFRC522 void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) return;
320 Часть III. Практическое применение Arduino /I чтение карты if ( ! mfrc522.PICC_ReadCardSerial()) return; // показать результат чтения UID и тип метки Serial.print(F("Card UID:")); dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); Serial.println(); Serial.print(F("PICC type: ")); byte piccType = infrc522.PICC_GetType(mfrc522.uid.sak); Serial.println(mfrc522.PICC_GetTypeName(piccType)); delayB000) ; } // Вывод результата чтения данных в НЕХ-виде void dump_byte_array(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); Загрузите скетч в плату Arduino. При поднесении меток (брелоков и карт) к модулю RFID-считывателя RC522 в монитор последовательного порта выводится UID (уникальный идентификатор) и тип метки (рис. 14.2). Рис. 14.2. Вывод в монитор последовательного порта уникальныого идентификатора и типа метки
Глава 14. RFID-идентификация 321 Электронный архив Полный вариант рассмотренного скетча находится в папке examples\U\_i4_0i сопровождающего книгу электронного архива (см. приложение 2). 14.2. Организация контроля доступа по RFID-меткам Создадим проект организации доступа по RFID-меткам. Arduino будет отправлять сигнал на срабатывание реле (например, для открытия электромеханического замка). Доступ осуществляется только для меток из списка. Монтажная схема подключения RFID-модуля RC522 к плате Arduino показана на рис. 14.3. Рис. 14.3. Схема соединений для организации доступа с помощью RFID-меток Приступим к написанию скетча. Уникальные идентификаторы карт или брелоков, по которым доступен вход, запишем в массив uidok: byte uidok[] [4] = { {0xllf0x22f0x33,0x44}f {0x11,0x22,0x33,0x45} }; Пин для подключения реле:. tdefine RELAY RC522 PIN 4
322 Часть III. Практическое применение Arduino В цикле будем считывать данные с поднесенной карты или брелока, затем сверять UID карты со списком разрешенных UTO и, если карта присутствует в списке, посылать на реле сигнал срабатывания (уровень low). Содержимое скетча приведено в листинге 14.2. # // подключение библиотек #include <LiquidCrystal.h> #include <SPI.h> #include <MFRC522.h> // пин для подключения реле #define RELAY_RC522_PIN 2 // пин для подключения динамика #define SPEAKER_PIN АО // уровни для включения/выключения реле ¦define RELAY_ON О #define RELAY_OFF 1 // Инициализация MFRC522 // константы подключения контактов SS и RST tdefine RST_PIN 3 #define SS_PIN 10 // Создание экземпляра MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN) ; // создаем экземпляр объекта дисплея LiquidCrystal lcd(8f9,4,5,6,7); // массив разрешенных uid byte uidok[][4]={ {OxEO, 0х2А, 0x87, OxlB}, {0xD9, OxFA, 0x90f 0x55}f {0x36, OxAE, 0x59, 0xA5 } void setup() { // инициализация дисплея led.beginA6,2); // очистить led.clear(); // инициализация SPI SPI.begin(); // инициализация MFRC522 mfrc522.PCD_Init(); // сконфигурировать вывод реле как OUTPUT pinMode(RELAY RC522 PIN,OUTPUT);
Глава 14. RFlD-идентификация 323 //и выключить digitalWrite(RELAY_RC522_PINf RELAY_OFF); // сконфигурировать вывод динамика pinMode(SPEAKER_PIN, OUTPUT); void loop () { if (mf rc522. PICC_IsNewCardPresent ()) { // чтение карты if ( mfrc522.PICC_ReadCardSerial()) { // показать результат чтения UID if(compare_uid(mfrc522.uid.uidByte/ mfrc522.uid.size)) { lcd.setCursorD,1); led.print("OK !!!"); tone(SPEAKER_PIN,494,300); delayE00); tone(SPEAKER_PIN,440,200); // включить реле digitalWrite (RELAY_RC522_PIN, RELAYJDN) ; delayD000) ; digitalWrite(RELAY_RC522_PIN, RELAY_OFF); } else { led.setCursorD,1); led.print("NO !!!"); tone(SPEAKER_PIN, 293, 300) ; delayE00) ; tone(SPEAKER_PIN,329,500); delayA000); Загрузим этот скетч в плату Arduino и проверим работу ограничения доступа с помощью RFID. Электронный архив Полный вариант рассмотренного скетча находится в папке examplesU4\_UJJ сопровождающего книгу электронного архива (см. приложение 2). 14.3. Запись информации на RFID-метку Ъразд. 14.1 и 14.2 мы создали скетчи для считывания UID (уникального идентификатора) RFID-меток, работающих на частоте 13,56 МГц, что позволяет использовать такие метки для организации систем контроля доступа. Но каждая метка, кроме UDD, имеет память, в которую можно записывать данные и из которой счи-
324 Часть III. Практическое применение Arduino Рис. 14.4. Метки Mirafe Ultralight тывать их. Рассмотрим самые простые работающие на частоте 13,56 МГц метки Mirafe Ultralight, выпускаемые в виде наклеек (рис. 14.4). Эти метки имеют 64 байта памяти, организованные в 16 блоков по 4 байта. Чтобы посмотреть содержимое памяти (дамп памяти) метки, соберем схему, представленную на рис. 14.1, и загрузим в плату Arduino скетч из листинга 14.3, осуществляющий чтение и вывод в последовательный порт дампа памяти метки. // Подключение библиотек #include <SPI.h> #include <MFRC522.h> // константы подключения контактов SS и RST #define RST_PIN 9 #define SS_PIN 10 // Инициализация MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN) ; void setup() { Serial.begin(9600); // инициализация последовательного порта SPI.beginO; // инициализация SPI mfrc522.PCD_Init(); // инициализация MFRC522- ShowReaderDetails(); // данные ридера Serial.println(F("Scan PICC to see UID, type, and data blocks...")); void loopO { // Ожидание прикладывания новой RFID-метки: if ( ! mfrc522.PICC_IsNewCardPresent()) return; // чтение карты if ( ! mfrc522.PICC_ReadCardSerial()) return; // показать результат чтения UID и тип метки // Вывод дампа памяти метки mfrc522.PICC_DumpToSerial (& (mfrc522.uid)) ;
Глава 14. RFID-идентификация 325 void ShowReaderDetails() { // Версия по для MFRC522 byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); Serial.print(F("MFRC522 Software Version: Ox")); Serial.print(v, HEX); if (v = 0x91) Serial.print(F(" = vl.O")); else if (v = 0x92) Serial.print(F(" = v2.0")); else Serial.print(F(" (unknown)")); Serial.println("ff); // Если 0x00 или OxFF, ошибка подключения if ((V == 0x00) || (v = OxFF)) { Serial.printIn(F("WARNING: Communication failure, is the MFRC522 properly connected?")); Загрузив скетч в плату Arduino, откроем монитор последовательного порта и при поднесении меток к считывателю увидим вывод дампа памяти метки Ultralight (рис. 14.5). Рис. 14.5. Вывод в монитор последовательного порта дампа памяти RFID-метки Mirafe Ultralight
326 Часть III. Практическое применение Arduino Электронный архив Полный вариант рассмотренного скетча находится в папке ехатр/евМ^и^З сопровождающего книгу электронного архива (см. приложение 2). Структура данных меток Mifare Ultralight приведена на рис. 14.6: ? В байтах SN0-SN7 — хранится UID метки; ? LockO, Lockl — блокировка страниц данных, например: • Охоооо — страницы 4-7 доступны для записи; • Oxffoo — блокированы страницы 4-11; • Oxfffo — блокированы страницы 4-15; ? ОТРО-ОТРЗ — однократно записываемые данные; ? DataO-Data47 — для записи данных. Рис. 14.6. Структура данных меток Mifare Ultralight Запись данных на метку и считывание данных с метки производятся поблочно. Как уже отмечалось ранее, размер блока меток Ultralight— 4 байта. Составим скетч отправки в метку по последовательному порту строки вида: *хх;уу;# где: ? хх — номер страницы; П уу — данные для первых двух байтов страницы хх.
Гпава 14. RFlD-идентификация 327 После получения и парсинга строки записываем данные в нужный блок. Скетч получения данных из метки по последовательному порту и запись данных на метку представлены в листинге 14.4. // Подключение библиотек iinclude <SPI.h> iinclude <MFRC522.h> // константы подключения контактов SS и RST fdefine RSTJPIN 9 idefine SS_PIN 10 // Инициализация MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN) ; // последовательный порт Arduino String inputStringO = ""; boolean stringCompleteO = false; unsigned int page_serial; unsigned int data_serial; void setup () { Serial.begin(9600); // инициализация последовательного порта SPI.begin(); // -инициализация SPI mfrc522.PCD_Init(); // инициализация MFRC522 ShowReaderDetails(); // данные ридера Serial.println(F("Scan PICC to see UID, type, and data blocks...")); inputStringO.reserveB00); void loop() { // if (stringCompleteO) { Serial.println(inputStringO); if(!parse_stringO()) {Serial.print("ERROR1");Serial.println(inputStringO);} else { page_serial=max(minA5,page_serial),7); data_serial=minA000,data_serial); } // clear the string: inputStringO = ""; stringCompleteO = false; } // Ожидание прикладывания новой RFID-метки: if (mfrc522.PICC_IsNewCardPresent().) { // чтение карты if (mfrc522.PICC ReadCardSerial()) {
328 Часть ///. Практическое применение Arduino if(data_serial>0 && page_serial>0) { byte buf[] = {0x00, 0x00, 0x00, 0x00}; buf[0]=highByte(data_serial); buf[1]=lowByte(data_serial); //Запись на карту на страницы 8,9 или 10-4 байта Serial.println(mfrc522.GetStatusCodeName(mfrc522.MIFARE_ Ultralight_Write(page_serial, buf, 4))); // Вьюод дампа данных mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); delayB000); // SerialEvent void serialEvent() { while (Serial.available()) { // получить очередной байт: char inChar = (char)Serial.read() // /n - конец передачи if (inChar == f#f) stringCompleteO = true; else // добавить в строку inputStringO += inChar; // парсинг строки последовательного порта Serial boolean parse_stringO() { int sl,s2; int lengthl=inputStringO.length(); if(inputStringO.charAt(O)!='*') return false; if(inputStringO.charAt(lengthl-1)!=';') return false; // for(int i=l;i<lengthl;i++) {if(inputStringO.charAt(i)==f;f) {sl=i;break;} } page_serial=inputStringO.substringA,si).tolnt(); // action for(int i=sl+l;i<lengthl;i++) {if(inputStringO.charAt(i)—f;') {s2=i;break;}
Глава 14. RFID-идентификация 329 data_serial=inputStringO.substring(sl+1,s2).tolnt(); return true; } void ShowReaderDetails() { // Версия по для MFRC522 byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); Serial.print(F(WMFRC522 Software Version: Ox")); Serial.print(v, HEX); if (V = 0x91) Serial.print(F(" = vl.O")); else if (v == 0x92) Serial.print(F(" = v2.0")); else Serial.print(F(" (unknown)")); Serial.println(""); // Если 0x00 или OxFF, ошибка подключения if ((v == 0x00) M (v == OxFF)) { Serial.printIn(F("WARNING: Communication failure, is the MFRC522 properly connected?")); g^yyg^^ Рис. 14.7. Запись данных в память RFID-метки Mirafe Ultralight
330 Часть III. Практическое применение Arduino Загружаем скетч в плату Arduino и проверяем его работу: отправляем строки из монитора последовательного порта и видим изменение данных в памяти метки (рис: 14.7). Электронный архив Полный вариант рассмотренного скетча находится в папке examples\14\_14_04 сопровождающего книгу электронного архива (см. приложение 2). Л АЛ. Проект «Говорящая фотография» Используем для проекта три фотографии характерных объектов. Приклеим к каждой фотографии с обратной стороны метку-наклейку Ultralight. При поднесении к фотографии (в районе метки) RFro-считывателя RC522 модуль DFPlayer Mini будет воспроизводить предварительно записанный для каждой фотографии МРЗ-файл с текстом. Схема соединений проекта показана на рис. 14.8. Рис. 14.8. Схема соединений проекта «Говорящая фотография» Прежде всего необходимо записать на SD-карту музыкальные файлы: 1. Подключим SD-карту через картридер к компьютеру и отформатируем ее в файловой системе FAT 16 или FAT32. 2. Создадим в корневом каталоге карты папку с названием трЗ. 3. Запишем в эту папку в качестве звуковых описаний для каждой фотографии соответствующие МРЗ-композиции и дадим им имена: 0001 .трЗ, 0002.трЗ и ОООЗ.трЗ.
Глава 14. RFlD-идентификация 337 С помощью скетча из листинга 14.4 запишем на 15-ю страницу каждой из трех меток UltraLight следующие данные: ?I *15,1;# — для первой метки; ? *15;2;# — для второй метки; ? *15;3;# — для третьей метки. А теперь напишем скетч получения данных из метки (байты 0 и 1 со страницы 15) и воспроизведения соответствующих МРЗ-файлов на модуле DFPlayer Mini (листинг 14.5). // Подключение библиотек tinclude <SPI.h> tinclude <MFRC522.h> // константы подключения контактов SS и RST tdefine RST_PIN 9 tdefine SS_PIN 10 // Инициализация MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN); // Подключение библиотек tinclude <SoftwareSerial.h> #include <DFPlayer_Mini_Mp3.h> // Создание объекта подключения по SoftwsreSerial SoftwareSerial mySerialB, 3); // RX, TX void setup() { Serial.begin(9600); // инициализация последовательного порта SPI.beginO; // инициализация SPI mfrc522.PCD_Init(); // инициализация MFRC522 ShowReaderDetails(); // данные ридера // запуск Software Serial mySerial.begin (9600); mp3_set_serial (mySerial); void loop() { // if (mf rc522. PICC_IsNewCardPresent ()) // Select one of the cards if (mfrc522.PICC_ReadCardSerial() ] int teksongl=getDataLabelA5); if (teksongl—Oxffff) { Serial.println("error");
332 Часть III. Практическое применение Arduino else if(teksongl>0) { teksong=teksongl; mp3_play(teksong); } Serial.println(teksongl); // данные карты void ShowReaderDetails() { // Версия по для MFRC522 byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); Serial.print(F("MFRC522 Software Version: Ox")); Serial.print(v, HEX); if (v = 0x91) Serial.print(F(" = vl.O")); else if (v == 0x92) Serial.print(F(" = v2.0")); else Serial.print(F(" (unknown)")) ; Serial.println(""); // Если 0x00 или OxFF, ошибка подключения if ((v == 0x00) || (v == OxFF)) { Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?")); // получение данных 15 страница байты 0 и 1 unsigned int getDataLabel(int pagel) { byte status; byte byteCount; byte bufferl[4]; byte i; int page; int offset; unsigned int res=0; // Read pages byteCount = sizeof(bufferl); page=pagel; status = mfrc522.MIFARE_Read(page, bufferl, SbyteCount); if (status != 1) { Serial.print(F("ltflFARE_Read() failed: ")); Serial.println(mfrc522.GetStatusCodeName(status)); return Oxffff;
Глава 14. RFID-идентификация 333 11 data res= (buf ferl [0] «8) +buf ferl [1] ; return res; Загружаем скетч в плату Arduino и проверяем его работу. Теперь каждая ваша фотография сможет рассказать о себе интересную историю. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\14\_U_05 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 15 Специальные возможности отдельных плат Arduino Существует несколько версий плат Arduino, специально разработанных для определенных задач. Рассмотрим их использование на конкретных примерах. 15.1. Использование Arduino Leonardo в качестве USB-устройства Возможно, наиболее важным моментом для любой платы Arduino является ее способность быть запрограммированной через последовательный порт USB. Это позволяет программировать Arduino без специального оборудования — такого, например, как программатор. В случае Arduino программатор уже встроен в плату. К тому же эта возможность обеспечивает прямое подключение к интегрированному универсальному синхронному/асинхронному приемнику и передатчику ATmega (USART). Используя этот интерфейс, вы можете обмениваться данными между платой Arduino и компьютером. У разных плат Arduino возможности последовательного соединения различны — как с точки зрения аппаратной реализации адаптеров USB-to-serial, так и с точки зрения программной поддержки тех или иных функций. Для начала необходимо понять разницу между последовательным портом и USB. Микроконтроллеры ATmega328, которыми укомплектованы Arduino Uno, имеют один аппаратный последовательный порт. Он включает выводы ТХ (передача) и RX (получение), к которым можно получить доступ на цифровых выводах 0 и 1. Плата Arduino оборудована загрузчиком, который позволяет программировать ее по последовательному интерфейсу. Это те выводы, которые «мультиплексированы» (т. е. выполняют более одной функции), — они служат и для линий приема/передачи кабеля USB. Но последовательный порт и порт USB несовместимы. Устранение этой проблемы на Arduino осуществляется двумя способами. Первый способ— добавление вторичной микросхемы-преобразователя, что применяется, например, на платах Arduino Uno. Второй — использование микроконтроллера, имеющего встроенный USB (например, микроконтроллер 32U4 в Arduino Leonardo).
Глава 15. Специальные возможности отдельных плат Arduino 335 Плата Arduino Leonardo была первой платой, имеющей только одну микросхему — микроконтроллер 32U4, — которая выполняет функции и программируемого пользователем микроконтроллера, и интерфейса USB. То есть Arduino Leonardo (и подобные платы Arduino) укомплектованы микроконтроллером, в который прямая передача USB уже встроена. В результате плата может более легко использоваться для эмуляции (имитации) USB-устройств, таких как клавиатура, мышь или джойстик. При этом обычный порт USART на ATmega не мультиплексирован с выводами интерфейса USB, поэтому связь с главным компьютером и вторичным последовательным устройством (таким как модуль GPS) может происходить одновременно. 15.1.1. Arduino Leonardo: имитация клавиатуры Для плат Arduino с прямой поддержкой USB в Arduino IDE, начиная с версии 1.01, включены два класса, обеспечивающие поддержку мыши и клавиатуры. Функции клавиатуры позволяют плате Arduino (Leonardo, Micro или Due) отправлять нажатия клавиш на подключенный компьютер. Вот список этих функций: ? Keyboard.begin () — запускает эмуляцию клавиатуры; ? Keyboard, end () — завершает эмуляцию клавиатуры; ? Keyboard.press о — имитация нажатия и удерживания функциональных клавиш клавиатуры; ? Keyboard.print о — отправляет последовательность кодов нажатых клавиш на компьютер; ? Keyboard, print in о — отправляет последовательность кодов нажатых клавиш на компьютер с добавлением кодов новой строки и возврата каретки; ? Keyboard, release о — завершает имитацию удерживания для функциональной клавиши клавиатуры; ? Keyboard.reieaseAii о — завершает имитацию нажатия и удерживания всех нажатых функциональных клавиш клавиатуры; ? Keyboard, write о — отправляет коды нажатия и отпускания клавиш на компьютер. Поддерживается также эмуляция клавиш-модификаторов (табл. 15.1). Таблица 15.1. Список клавиш-модификаторов объекта Keyboard Ключ KEY_LEBT_CTRL KEY_LEFT_SHIFT KEY_LEFT_ALT KEY_LEFT_GUI KEY_RIGHT_CTRL Шестнадцатеричное значение 0x80 0x81 0x82 0x83 0x84 Десятичное значение 128 129 130 131 132
336 Часть III. Практическое применение Arduino Таблица 15.1 (окончание) Ключ KEY_RIGHT_SHIFT KEY_RIGHT_ALT KEY_RIGHT_GUI KEY_UP_ARROW KEY_DOWN_ARROW KEY_LEFT_ARROW KEY_RIGHT_ARROW Key Backspace Key_Tab Key Return KEY_ESC Key Insert Key Delete KEY_PAGE_UP KEY_PAGE_DOWN KEY_HOME Key End KEY_CAPS_LOCK Key_Fl Key_F2 Key_F3 Key_F4 Key_F5 Key_F6 Key_F7 Key_F8 Key_F9 Key_F10 Key_Fll Key_F12 Шестнадцатеричное значение 0x85 0x86 0x87 OxDA 0xD9 0xD8 0xD7 0xB2 ОхВЗ OxBO OxBl OxDl 0xD4 0xD3 0xD6 0xD2 0xD5 OxCl 0xC2 ОхСЗ 0xC4 0xC5 0xC6 0xC7 0xC8 0xC9 OxCA OxCB OxCC OxCD Десятичное значение 133 134 135 218 217 216 215 178 179 176 177 209 212 211 214 210 213 193 194 195 196 197 198 199 200 201 202 203 204 205
Глава 15. Специальные возможности отдельных плат Arduino 337_ 15.1.2. Блокируем клавиатуру с наступлением темноты Создадим простой пример использования объекта Keyboard — например, реализуем блокировку клавиатуры компьютера при наступлении темноты. Для этого подсоединим к аналоговому входу АО платы Arduino Leonardo фоторезистор и при наступлении темноты будем отправлять на компьютер комбинацию клавиш, блокирующую компьютер (для Windows — это комбинация <Windows>+<L>). Содержимое скетча представлено в листинге 15.1. const int LIGHT =A0; // Датчик освещенности на вывод 1 const int THRESHOLD =200; // Значение с датчика освещенности // для блокировки компьютера void setup {) { Keyboard.begin(); } void loopO { int brightness = analogRead(LIGHT); // Чтение данных датчика if (brightness < THRESHOLD) { Keyboard.press(KEY_LEFT_GUI); Keyboard.press('1'); delayA00); Keyboard.releaseAll(); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\15\_15JI сопровождающего книгу электронного архива (см. приложение 2). 15.1.3. Arduino Leonardo: имитация компьютерной мыши Функции мыши позволяют плате Arduino Leonardo управлять положением мыши на подключенном компьютере. Вот список этих функций: ? Mouse. begin () — запускается эмуляция мыши на подключенном компьютере; ? Mouse. click () — эмулирует нажатие и отпускание кнопки мыши: • mouseleft (по умолчанию) — левая кнопка; • mouseright — правая кнопка; • mouse_middle — средняя кнопка;
338 Часть III. Практическое применение Arduino ? Mouse. end () — завершается эмуляция мыши на подключенном компьютере; ? Mouse.move о — перемещение курсора на подключенном компьютере (относительно текущего положения курсора); ? Mouse.press о — эмуляция нажатия и постоянного удерживания кнопки мыши, нажатие отменяется функцией Mouse. release (): • mouseleft (по умолчанию) — левая кнопка; • mouseright — правая кнопка; | i • mouse_middle — средняя кнопка; ? Mouse. release () — отмена нажатия и постоянного удерживания кнопки мыши; ? Mouse.isPressedO — проверяет текущее состояние кнопок мыши. Используя двухосевой джойстик (рис. 15.1) и несколько кнопок, с помощью Arduino Leonardo можно сделать свою собственную мышь! Джойстик будет контролировать местоположение указателя мыши, а кнопки — выполнять функции левой, средней и правой ее кнопок. Рис. 15.1. Двухосевой джойстик Монтажная схема эмулятора компьютерной мыши на Arduino Leonardo представлена на рис. 15.2. Рис. 15.2. Монтажная схема подключения эмулятора компьютерной мыши
Глава 15. Специальные возможности отдельных плат Arduino ЗЗЦ Содержимое скетча для эмулятора компьютерной мыши представлено в листинге 15.2. const int LEFT_BUTTON=4; // Вход для левой кнопки мыши const int MIDDLEJ3UTTON=3; // Вход для средней кнопки мыши const int RIGHT_BUTTON =2; // Вход для правой кнопки мыши const int X_AXIS=0; // Аналоговый вход для оси х джойстика const int Y_AXIS=1; // Аналоговый вход для оси у джойстика void setup () { Mouse.begin(); } void loop () { int xVal=readJoystick(X_AXIS); // Получить отклонение // джойстика по оси х int yVal=readJoystick(Y_AXIS); // Получить отклонение // джойстика по оси у Mouse.move(xVal, yVal, 0); // Перемещаем мышь readButton(LEFT_BUTTON/MOUSE_LEFT); // Чтение состояния левой кнопки readButton(MIDDLE__BUTTONfMOUSE_MIDDLE); // Чтение состояния средней readButton(RIGHT_BUTTON, MOUSE_RIGHT); // Чтение состояния правой delayE); } // Чтение значения джойстика, масштабирование int readJoystick(int axis) { int val = analogRead(axis); // Чтение аналогового значения val = map(val, 0, 1023, -10, 10); // Масштабирование значения if (val <= 2 && val >= -2) // Убрать дрейф мыши return 0; else // Вернуть значение return val; } // Чтение состояния кнопок и отправка команд мыши void readButton(int pin, char mouseCommand) { // Если кнопка нажата, эмулируем нажатие, если она еще не была нажата if (dfgitalRead(pin) — LOW) { if (!Mouse.isPressed(mouseCommand))
340 Часть III. Практическое применение Arduino Mouse.press (mouseCoinmand); } } // Отпустить нажатие мыши else { if (Mouse.isPressed(mouseConimand)) { Mouse, release (mouseCoinmand) ; Функция readJoystickO создана для считывания значений джойстика и масштабирования их. Каждая ось джойстика имеет ряд значений: от 0 до 1024, полученных от входа аналого-цифрового преобразователя (АЦП). Значения от 0 до 1023 масштабируем к значениям от -10 до 10. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\15\_15JJ сопровождающего книгу электронного архива (см. приложение 2). 15.2. Плата Ardumo Esplora Плата Arduino Esplora спроектирована на основе Arduino Leonardo и отличается от всех предыдущих плат Arduino наличием множества встроенных и готовых к использованию датчиков. Дизайн платы Arduino Esplora напоминает дизайн обычного геймпада с аналоговым джойстиком слева и четырьмя кнопками справа (рис. 15.3). Рис. 15.3. Плата Arduino Esplora
Глава 15. Специальные возможности отдельных плат Arduino 341 Arduino Esplora обладает следующими встроенными средствами ввода и вывода (рис. 15.4): 1. Аналоговый джойстик с кнопкой. 2. Четыре кнопки. 3. Линейный потенциометр. 4. Микрофон. 5. Датчик освещенности. 6. Датчик температуры. 7. Трехосевой акселерометр. 8. Зуммер для генерации звукового сигнала прямоугольной формы. 9. RGB-светодиод. 10. Два Tinkerkit-входа для подключения Tinkerkit-модулей датчиков с 3-пиновыми разъемами. 11. Два Tinkerkit-выхода для подключения Tinkerkit-модулей приводов с 3-пиновыми разъемами. 12. Разъем подключения TFT-дисплея, считывающего устройства SD-карты или других устройств, использующих протокол SPI. On led lied Txled I IRxled 11 Ю Output 1U Unkerkit Connectors ATMEGA32U4 8 Buzzer- sensor) Ж 4 Switch 1 Analog .-*- with central button 12 ICD 4 Microphone connector (TFT) 6 Temperature I Sensor linear Potentiometer 9 RGB led ^3Axis 7 Accelerator Рис. 15.4. Средства ввода и вывода платы Arduino Esplora Как и на плате Leonardo, в Esplora используется AVR-микроконтроллер ATmega32U4 с кварцевым резонатором 16 МГц. Arduino Esplora может определяться как обычная клавиатура или мышь и с помощью библиотек Keyboard и Mouse может быть запрограммирована на управление этими устройствами ввода.
342 Часть ///. Практическое применение Arduino Чтобы упростить написание программ для платы Esplora, существует специальная библиотека Esplora, которая содержит методы для считывания данных с датчиков и отправки информации на встроенные устройства вывода, а также высокоуровневые методы, возвращающие уже обработанные данные, — например, градусы по Фаренгейту или Цельсию, вычисленные по показаниям датчика температуры. Эта библиотека обеспечивает простой доступ к устройствам вывода — например, при отправке значений RGB-светодиоду. В примерах работы с Esplora в Arduino IDE (рис. 15.5) показаны основные функции входов и выходов устройства — с их помощью удобно экспериментировать с платой Arduino Esplora и познавать ее возможности: ? EsploraBlink — мерцание встроенного RGB-светодиода. ? EsploraAccelerometer — считывание показаний акселерометра; ? EsploraJoystickMouse — управление курсором компьютера с помощью джойстика; ? EsploraLedShow — световое шоу с использованием джойстика и слайдера; ? EsploraLedShow2 — изменение цвета встроенного RGB-светодиода с использованием микрофона, потенциометра и датчика освещенности; ? EsploraLightCalibrator — определение освещенности; Рис. 15.5. Примеры библиотеки Esplora
Глава 15. Специальные возможности отдельных плат Arduino 343 ? EsploraMusic — немного музыки в исполнении Arduino Esplora; ? EspIoraSoundSensor — обработка сигнала со встроенного микрофона; ? EsploraTemperatureSensor — считывание показаний температурного датчика и расчет температуры в градусах Фаренгейта и Цельсия. Внимательно изучаем эти примеры, а затем приступаем к написанию своих программ. 15.2.1. Arduino Esplora: установка цветов RGB-светодиода Напишем скетч для установки цвета RGB-светодиода с помощью трех кнопок: SWITCH2, SWITCH3, SWITCH4. Если нажата кнопка SWITCH2 — горит красный светодиод, не нажата — красный светодиод потушен. Если нажата кнопка SWITCH3 — горит зеленый светодиод, не нажата — зеленый светодиод потушен. Если нажата кнопка SWITCH4 — горит синий светодиод, не нажата — синий светодиод потушен. Сначала подключаем библиотеку Esplora: iinclude <Esplora.h> Создадим переменные для значений красного, зеленого и синего цветов RGB-светодиода: int red, green, blue; В цикле loop () проверяем данные с кнопок и устанавливаем значения для соответствующих переменных: red, green, blue: if (Esplora.readButton(SWITCH_2) = HIGH) red = 255; else red = 0; if (Esplora.readButton(SWITCH_3) — HIGH) green = 255; else green = 0; if (Esplora.readButton(SWITCH_4) — HIGH) blue = 255; else blue = 0; И устанавливаем цвета RGB-светодиода: Esplora.writeRGB(red, green, blue); Полное содержимое скетча показано в листинге 15.3. Загружаем его в плату Arduino и управляем цветами RGB-светодиода нажатием на кнопки SWITCH2, SWITCH3, SWITCH4.
344 Часть III. Практическое применение Ardumo // Подключение библиотеки Esplora. #include <Esplora.h> // Переменные для значений красного, зеленого и синего цветов RGB-светодиода int red, green, blue; void setup() { void loop() { // получение данных с кнопок if (Esplora.readButton(SWITCH_2) == LOW) //нажата red = 255; else red = 0; if (Esplora.readButton(SWITCH_3) == LOW) //нажата green = 255; else green = 0; if (Esplora.readButton(SWITCH_4) == LOW) //нажата blue = 255; else blue = 0; // Устанавливаем цвета RGB-светодиода: Esplora.writeRGB(red, green, blue); Электронный архив Полный вариант рассмотренного скетча находится в папке examp/esW5W5_03 сопровождающего книгу электронного архива (см. приложение 2). 15.2.2. Arduino Esplora: создание игры Рассмотрим применение Arduino Esplora с TFT-дисплеем в качестве игровой консоли и создадим для этого игру «Змейка» (рис. 15.6). Змейка на экране дисплея будет представлять собой линейку из множества квадратов размером 8x8, соединенных между собой. Данные о звеньях мы станем сохранять в массиве, каждый из элементов которого — координаты х и у левого верхнего угла каждого звена: // структура для описания координаты одного звена 8x8 struct Pos { int x; int у;
Глава 15. Специальные возможности отдельных плат Arduino 345 II массив всех звеньев змейки Pos snake[40]={{40,0},{32,0},{24,0},{16,0},{8,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0f0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0Ы0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} }; Переменная — указатель на хвост змейки (размер змейки): int offsetsnake=6; Переменные для направления движения змейки и коэффициент для фактического смещения: // перемещение по осям х и у и коэффициент (шаг) int dX=l; // -1, 0, 1 int dY=0; // -1, 0, 1 int kXY=8; И переменные для скорости — времени, после которого происходит очередное изменение положения змейки: // скорость int speedsnake=1000; unsigned long millissnake=0; Рис. 15.6. Игра «Змейка»
346 Часть III. Практическое применение Arduino Кнопки SWITCH1, SWITCH2, SWITCH3, SWITCH4 будут использоваться для изменения направления змейки, которое описано в процедуре setdir (): // получение смещения по осям х и у // получение данных с кнопок if (Esplora.readButton(SWITCH_l) — LOW) //нажата setdir@,1); else if (Esplora.readButton(SWITCH_2) — LOW) //нажата setdir (-1, Ob- else if (Esplora.readButton(SWITCHJ3) = LOW) //нажата setdir@,-1); else if (Esplora.readButton(SWITCH_4) — LOW) //нажата setdir A, Ob- else // установить новое направление движения void setdir(int x,int y) { int xl,yl; xl=x;yl=y; if((dX+x)—0 && abs(x)>0) {yl=y;xl=dX;} if((dY+y)=0 && abs(y)>0) {xl=x;yl=dY;} // установить dX=xl;dY=yl; } Полное содержимое скетча показано в листинге 15.4. Загрузим скетч в плату Arduino Esplora и проверим управление змейкой с помощью кнопок. // Подключение библиотеки для работы с TFT #include <TFT.h> #include <SPI.h> // Подключение библиотек для работы с Esplora #include <Esplora.h> // структура для описания координаты одного звена 8x8 struct Pos { int x; int у; }; // массив всех звеньев змейки Pos snake[40] = {{40,0Ы32,0},{24,0},{16,0},{8,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0},
Глава 15. Специальные возможности отдельных плат Arduino 347 {0,0}, {0,0}, {0,0},{p,0},-{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0f0}f{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} }; int offsetsnake=6; // перемещение по осям х и у и коэффициент (шаг) int dX=l; // -1, 0, 1 int dY=0; // -1, 0, 1 int kXY=8; // скорость int speedsnake=1000; unsigned long millissnake=0; void setup () { // Serial.begin(9600); // EsploraTFT. begin () ; // очищаем экран - черный цвет EsploraTFT.background@,0,0); // пауза delayA000); void loop () { // получение смещения по осям х и у // получение данных с кнопок if (Esplora.readButton(SWITCHJL) == LOW) //нажата setdir@,l); else if (Esplora.readButton(SWITCH_2) == LOW) //нажата setdir(-1,0); else if (Esplora.readButton(SWITCH_3) = LOW) //нажата setdir@,-1); else if (Esplora.readButton(SWITCH_4) == LOW) //нажата setdirA,0); else //// изменение положения змейки if (millis()-millissnake>=speedsnake) { // стереть предыдущее EsploraTFT.fill@, 0, 0); for (int i=0;i<offsetsnake;i++) {. EsploraTFT.rect(snake[i].x, snake[i].y, 8, 8);
348 Часть III. Практическое применение Arduino //// новая позиция для змейки // остальные for(int i=offsetsnake-l;i>0;i—) { snake[i].x=snake[i-l].x; snake[i].y=snake[i-l].y; } // первая snake[0].x=snake[0].x+dX*kXY; snake[0].y=snake[0].y+dY*kXY; // нарисовать новое EsploraTFT.fillB55, 0, 0) ; for(int i=0;i<offsetsnake;i++) { EsploraTFT.rect(snake[i].x, snake[i].у, 8, 8); } millissnake=millis(); // установить новое направление движения void setdir(int x,int у) { int xl,yl; xl=x;yl=y; if((dX+x)—0 && abs(x)>0) {yl=y;xl=dX;} if((dY+y)==0 && abs(y)>0) {xl=x;yl=dY;} // установить dX=xl;dY=yl; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i5\_i5_04 сопровождающего книгу электронного архива (см. приложение 2). Добавим в скетч генерирование корма для змейки. Нам необходим массив для хранения координат корма с максимальным количеством 10: // массив для корма Pos food[10]-{{0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} }; int counterfood=0; Через время speedf ood генерируем корм и отображаем его на экране: //// появление корма if(millis()-millisfood>=speedfood && counterfood<10) { // генерация случайных х и у int xf=random@,20)*8; int уf=randomB,15)* 8;
Глава 15. Специальные возможности отдельных плат Arduino ; 349 // проверка отсутствия в списке корма for(int i=0;i<10;i++) { if(food[i].x==xf && food[i].y==yf) ; else if(food[i].x==0 && food[i].y==0){ //не попадает на змейку if(nosnake(xf,yf)) { // добавить в массив и на экран addfood(xf,yf, i); // звук Esplora.toneB62,200) ; counterfood++; i=10; millisfood=*millis () ; } В процедуре nosnake () проверяем отсутствие сгенерированного значения координат в базе — не попадает ли корм на змейку, заносим данные в массив food и выводим красный квадрат на дисплей: // проверка непопадания корма на змейку boolean nosnake(int x,int у) { boolean ok=true; for(int i=0;i<40;i++) { if (snake [i] .x==x && snake[i] .y=y) ok=false; } return ok; } // добавить корм в массив и вывести void addfood(int x,int у, int pos) { food[pos].x=x; food[pos].y=y; EsploraTFT.filMO, 0, 255); EsploraTFT.rect(x, y, 8, 8); } В цикле после изменения положения змейки проверяем, не съела ли змейка корм — не произошло ли совпадение ее головы с координатами всех позиций корма: // проверка — съела змейка корм? boolean atefood(int x,int у) { boolean ok=false; for(int i=0;i<10;i++) { if(food[i],x==x && food[i].y==y) { food[ij.x=0;food[i].y=0;
350 Часть III. Практическое применение Arduino counterfood—; ok=true; Serial.printIn(i); return ok; } Полное содержимое скетча показано в листинге 15.5. Загрузим скетч в плату Arduino Efcplora и проверим его работу. // Подключение библиотеки для работы с TFT #include <TFT.h> #include <SPI.h> // Подключение библиотек для работы с Esplora #include <Esplora.h> // структура для описания координаты одного звена 8x8 struct Pos { int x; int у; }; // массив всех звеньев змейки Pos snake[40]={{40,16},{32,16},{24,16},{16,16},{8,16}, {0,16},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} }; int offsetsnake=6; // массив для корма Pos food[10]={{0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} }; int counterfood=0; // перемещение по осям х и у и коэффициент int dX=l; // -1, 0, 1 int dY=0; // -1, 0, 1 int kXY=8; // скорость int speedsnake=300; unsigned long millissnake=0;
Глава 15. Специальные возможности отдельных плат Arduino 357 // время появления нового корма unsigned long speedfood=5000; unsigned long millisfood=0; // массив символов для вывода времени на экран char printout [4] ; void setup () { // Serial.begin(9600); // EsploraTFT.beginO ; // очищаем экран - черный цвет EsploraTFT.background@,0,0); // пауза EsploraTFT.fillB55, 255, 0); EsploraTFT.rect@, 16, 160, 128); void loop () { // получение смещения по осям х и у // получение данных с кнопок if (Esplora.readButton(SWITCH_l) = LOW) //нажата setdir@,l); else if (Esplora.readButton(SWITCH_2) == LOW) //нажата setdir(-l,0); else if (Esplora.readButton(SWITCH_3) == LOW) //нажата setdir@,-l); else if (Esplora.readButton(SWITCH_4) == LOW) //нажата setdir(l,0); else ; //// изменение положения змейки if (millis() -millissnake>=speedsnake) { // стереть предыдущее EsploraTFT.fillB55, 255, 0); for(int i=0;i<offsetsnake;i++) { EsploraTFT.rect(snake[i].x, snake[i].y, 8, 8); } //// новая позиция для змейки // остальные for(int i=offsetsnake-l;i>0;i—) { snake[i].x=snake[i-l].x; snake[i],y=snake[i-l].y; } // первая snake[0].x=snake[0].x+dX*kXY; snake[0].y=snake[0].y+dY*kXY;
352 Часть III. Практическое применение Arduino // нарисовать новое EsploraTFT.fillB55, 0, 0); for(int i=0;i<offsetsnake;i++) { EsploraTFT.rect(snake[i].x, snake[i].y, 8, 8); } // проверка - съела змейка корм? if(atefood(snake[0].x,snake[0].y)) { Esplora.toneD94,200); delayB00); Esplora.toneC49,200); offsetsnake++; } // новый отсчет millissnake millissnake=millis(); } //// появление корма if (millis()-millisfood>=speedfood && counterfood<10) { // генерация случайных х и у int xf=random@,20)*8; int yf=randomB,15)*8; // проверка отсутствия в списке корма for(int i=0;i<10;i++) { if (food[i] .x=xf && food[ij .y==yf) r else if(food[i].x==0 && food[i].y==0){ //не попадает на змейку if(nosnake(xf,yf)) { // добавить в массив и на экран addfood(xf,yf,i); // звук Esplora.toneB62,200); counterfood++; i=10; millisfood=millis() ; // установить новое направление движения void setdir(int x,int у) { int xl,yl; xl=x;yl=y; if((dX+x)==0 && abs(x)>0) {yl=y;xl=dX;} if((dY+y)==0 && abs(y)>0) {xl=x;yl=dY;}
Глава 15. Специальные возможности отдельных плат Arduino 353 II установить dX=xl;dY=yl; } // добавить корм в массив и вывести void addfood(int x,int у, int pos) { food[pos].x=x; food[pos].y=y; EsploraTFT.fill@, 0, 255); EsploraTFT.rect(x, y, 8, 8) ; } // проверка непопадания корма на змейку boolean nosnake(int x,int у) '{ boolean ok=true; for(int i=0;i<40;i++) { if(snake[i].x==x && snake[i].y==y) ok=false; } return ok; } // проверка — съела змейка корм? boolean atefood(int x,int у) { boolean ok=false; for(int i=0;i<10;i++) { if(food[i].x==x && foodfi].y==y) { food[i].x=0;food[i].y=0; counterfood—; ok=true; Serial.printIn(i); return ok; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\1&_15JM сопровождающего книгу электронного архива (см. приложение 2). Далее реализуем проверку выхода змейки за пределы экрана или на себя — в этом случае заканчиваем игру: // проверка выхода из границ boolean outborder(int x,int у) { boolean ok=false; if(x<0 || x>152 || y<16 || y>120) { ok=true; } return ok;
354 Часть III. Практическое применение Arduino I/ проверка — не съела ли себя boolean outsnake(int x,int у) { boolean ok=false; for(int i=l;i<40;i++) { if(x==snake[i].x && y==snake[i].y) { ok=true; return ok; } А также реализуем табло для вывода времени игры, суммы набранных очков, текущей длины змейки и количества корма на площадке: // табло void tabloO { // очистить EsploraTFT.filMO, 0, 0) ; EsploraTFT.rect@, 0, 100, 16); // текст // цвет текста EsploraTFT.strokeB55,255, 0); // шрифт для текста //EsploraTFT.setTextSizeA); // выводим текст • // корма на поле String s = String(counterfood); s.toCharArray(printout,4); EsploraTFT.text(printout,20,2); EsploraTFT.strokeB55,255,0); // длина змейки s = String(offsetsnake); s.tbCharArray(printout,4); EsploraTFT.text(printout,40,2); EsploraTFT.strokeB55,255,0); // сумма очков s = String(sum) ; s.toCharArray(printout,4); EsploraTFT.text(printout,70,2); EsploraTFT.strokeB55,255,0); void settime() { // очистить EsploraTFT.fill@, 0, 0); EsploraTFT.rectA00, 0, 60, 16); // вывод времени String s = String(seconds/60); s.toCharArray(printout,4);
Глава 15. Специальные возможности отдельных плат Arduino 355 if(seconds/60<l0) { EsploraTFT.text(",110,2); EsploraTFT.text(printout,116,2); } else { EsploraTFT.text(printout,110,2); } EsploraTFT.text(":",122,2); s = String(seconds%60); s.toCharArray(printout,4); if(seconds%60<10) { EsploraTFT.text(",128,2); EsploraTFT.text(printout,134,2); } else { EsploraTFT. text (printout, 128,2); } EsploraTFT.strokeB55,255,0); Полное содержимое скетча показано в листинге 15.6. Загрузим скетч в плату Arduino Esplora и проверим его работу. При желании вы можете внести в игру «Змейка» собственные изменения. // Подключение библиотеки для работы с TFT iinclude <TFT.h> tinclude <SPI.h> // Подключение библиотек для работы с Esplora iinclude <Esplora.h> // структура для .описания координаты одного звена 8x8 struct Pos { int x; int у; }; // массив всех звеньев змейки Pos snake[40]={{40,16},{32,16},{24,16},{16,16},{8,16}, {0,16},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0}
356 Часть III. Практическое применение Arduino int offsetsnake=6; // массив для корма Pos food[10]={{0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0} >; int counterfood=0; // перемещение по осям х и у и коэффициент int dX=l; // -1, 0, 1 int dY=0; // -1, 0, 1 int kXY=8; // скорость int speedsnake=300; unsigned long millissnake=0; // время появления нового корма unsigned long speedfood=5000; unsigned long millisfood=0; // массив символов для вывода времени на экран char printout[4]; // сумма набранных очков int sum=100; // время игры - секунд unsigned long seconds=0; unsigned long millisseconds=0; void setup(){ // Serial.begin(9600); // EsploraTFT.begin(); // очищаем экран - черный цвет EsploraTFT.background@,0,0); // пауза EsploraTFT.fillB55, 255, 0) ; EsploraTFT.rect@, 16, 160, 128); tablo(); void loop(){ // отсчет времени if (millis()-millisseconds>=1000) { seconds++; settime (); millisseconds=mLllis(); } // получение смещения по осям х и у // получение данных с кнопок if (Esplora.readButton(SWITCH_l) = LOW) //нажата setdir@,l);
Глава 15. Специальные возможности отдельных плат Arduino 357 else if (Esplora.readButton(SWITCH_2) == LOW) //нажата setdir(-1,0); else if (Esplora.readButton(SWITCH_3) == LOW) //нажата setdir@,-1); else if (Esplora.readButton(SWITCH_4) == LOW) //нажата setdirA,0); else //// изменение положения змейки if(millis()-millissnake>=speedsnake) { // стереть предыдущее EsploraTFT.fillB55, 255, 0) ; for(int i=0;i<offsetsnake;i++) { EsploraTFT.rect(snake[i].x, snake[i].y, 8, 8); } //// новая позиция для змейки // остальные for(int i=offsetsnake-l;i>0;i—) { snake[i].x=snake[i-1].x; snake[i].y=snake[i-1].у; } // первая snake[0].x=snake[0].x+dX*kXY; snake[0].y=snake[0].y+dY*kXY; // нарисовать новое EsploraTFT.fillB55, 0, 0); for(int i=0;i<offsetsnake;i++) { EsploraTFT.rect(snake[i].x, snake[i].y, 8, 8); } // проверка - съела змейка корм? if(atefood(snake[0].x,snake[0],y)) { Esplora.toneD94,200); delayB00); Esplora.toneC49,200); setsumC0+offsetsnake); tablo(); offsetsnake++; } // проверка выхода из границ if(outborder(snake[0].x,snake[0].y)) { endgame(); newgame(); tablo(); } // проверка — не съела ли себя if(outsnake(snake[0].x,snake[0].y)) { endgame();
358 Часть III. Практическое применение Arduino newgame () ; tablo(); } // новый отсчет millissnake millissnake=millis(); } //// появление корма if(millis()-millisfood>=speedfood && counterfood<l0) { // генерация случайных х и у int xf=random@,20) *8 ; int yf=randomB,15)*8; // проверка отсутствия в списке корма for(int i=0;i<10;i++) { if(food[i].x==xf && food[i].y==yf) r else if(food[i].x==0 && food[i].y==0){ //не попадает на змейку if(nosnake(xf,yf)) { // добавить в массив и на экран addfood(xf,yf,i); setsum(-5*counterfood); tablo(); // звук Esplora.toneB62,200); counte r food++; tablo(); millisfood^millis(); // установить новое направление движения void setdir(int x,int y) { int xl,yl; xl=x;yl=y; if((dX+x)==0 && abs(x)>0) {yl=y;xl=dX;} if((dY+y)=0 && abs(y)>0) {xl=x;yl=dY;} // установить dX=xl;dY=yl; } // добавить корм в массив и вывести void addfood(int x,int y, int pos) { food[pos].x=x;
Глава 15. Специальные возможности отдельных плат Arduino 359 food[pos].y=y; EsploraTFT.filMO, 0, 255); EsploraTFT.rect(x, у, 8, 8); } // проверка непопадания корма на змейку- boolean nosnake(int x,int у) { boolean ok=true; for(int i=0;i<40;i++) { if (snake [i] ,x=x && snake[i] .y=y) ok=false; } return ok; } // проверка — съела змейка корм? boolean atefood(int x,int у) { boolean ok=false; for(int i=0;i<10;i++) { if (food[i] .x==x && food[i] .y=y) { food[i].x=0;food[i].y=0; counterfood—; ok=true; Serial.printIn(i); return ok; } // проверка выхода из границ boolean outborder(int x,int y) { boolean ok=false; if(x<0 || x>152 || y<16 || y>120) { ok=true; } return ok; } // проверка — не съела ли себя boolean outsnake(int x,int у) { boolean ok=false; for(int i=l;i<40;i++) { if (x==snake[i] .x && y=snake[i] .y) { ok=true; return ok; } // табло void tabloO {
360 Часть III. Практическое применение Arduino I/ очистить EsploraTFT.filMO, 0, 0) ; EsploraTFT.rect@, 0, 100, 16); // текст // цвет текста EsploraTFT.strokeB55,255,0); // шрифт для текста //EsploraTFT.setTextSizeA); // выводим текст // корма на поле String s = String(counterfood); s.toCharArray(printout,4); EsploraTFT.text(printout,20,2); EsploraTFT.strokeB55,255,0) ; // длина змейки s = String(offsetsnake); s.toCharArray(printout,4); EsploraTFT.text(printout,40,2); EsploraTFT.strokeB55,255,0) ; // сумма очков s = String(sum); s.toCharArray(printout,4); EsploraTFT.text(printout,70,2); EsploraTFT.strokeB55,255,0) ; } // подсчет количества очков void endgame() { // очистить экран EsploraTFT.background@,0,0); EsploraTFT.fillB55, 255, 0) ; // цвет текста EsploraTFT.strokeB55,255, 0); EsploraTFT.text("GAME END",50,40); // сумма очков EsploraTFT.text("sum =",50,70); String s = String(sum); s.toCharArray(printout,4); EsploraTFT.text(printout,86,70); // ожидаем нажатия кнопки while(Esplora.readButton(SWITCH_1)==HIGH && Esplora.readButton(SWITCH_2)==HIGH & & Esplora.readButton(SWITCH_3)==HIGH & & Esplora.readButton(SWITCH_4)==HIGH ) // начать заново void newgame() {
Глава 15. Специальные возможности отдельных плат Arduino 361 // очистить экран EsploraTFT.background@,0,0); EsploraTFT.fillB55, 255, 0); EsploraTFT.rect@, 16, 160, 128); // массив snake for(int i=0;i<1 food[i].x=0; food[i],y=0; } counterfood=0; // массив food for(int i=0;i<4 snake[i].x=0; snake[i].y=0; } snake[0].x=40;snake[0].y=16;• snake[1].x=32;snake[1 ].y=16; snake[2].x=24;snake[2].y=16; snake[3].x=16;snake[3].y=16; snake[4].x=8;snake[4].y=16; snake[5].x=0;snake[5].y=16; offsetsnake=6; // направление dX=l; dY=0; // sum=100; seconds=0; } // подсчет количества очков void setsum(int add) { sum=sum+add; void settiine () { // очистить EsploraTFT.fill@, 0, 0); EsploraTFT.rectA00, 0, 60, 16); // вывод времени String s = String(seconds/60); s.toCharArray(printout,4); if(seconds/60<10) { EsploraTFT.text(", 110,2) ; EsploraTFT.text(printout,116,2); } else { EsploraTFT.text(printout,110,2);
362 Часть III. Практическое применение Arduino EsploraTFT.text(":",122,2); s = String(seconds%60); s.toCharArray(printout, 4); if(seconds%60<10) { EsploraTFT.text(",128,2) ; EsploraTFT.text(printout,134,2); else { EsploraTFT. text (printout, 128,2) ; EsploraTFT.strokeB55,255,0); } Электронный архив Полный вариант рассмотренного скетча находится в папке examplesWS^ISjoe сопровождающего книгу электронного архива (см. приложение 2). 15.3. Плата Arduino LilyPad Плата Arduino LilyPad была разработана и создана дизайнером Leah Buechley совместно со SparkFun для использования с предметами одежды и текстиля. Arduino LilyPad можно пришивать к ткани и с помощью токопроводящих нитей подключать питание, датчики или исполнительные устройства. Электронная схема, собранная на ткани, включая саму плату Arduino LilyPad, не боится стирки — ее можно стирать вручную, естественно, предварительно отключив питание. Печатная плата LilyPad Arduino имеет форму круга диаметром около 50 мм. Плата выполнена на микроконтроллерах ATmegal68V или ATmega328V. Напряжение питания платы — в интервале от 2,7 до 5,5 В. При отрицательном питании или большем чем 5,5 В плата может выйти из строя. Существуют три варианта этой платы: ? LilyPad Arduino 328 (рис. 15.7) — на базе микроконтроллера ATmega328; ? LilyPad Arduino USB (рис. 15.8) — на базе микроконтроллера ATmega32U4. Этот вариант отличается наличием USB-порта для связи с компьютером и разъема для литиевой батареи; ? LilyPad Simple Snap (рис. 15.9)— благодаря специальным контактам из кнопок может отстегиваться от схемы, содержит встроенный литиевый аккумулятор. Рассмотрим плату LilyPad Arduino 328 (см. рис. 15.7), построенную на базе микроконтроллера ATmega328. Плата имеет 22 контакта (рис. 15.10). Контакты + и - предназначены для питания платы. Остальные контакты аналогичны контактам Arduino Uno. Также на плате присутствуют штырьковые контакты для подключения переходника USB-Serial, необходимого для загрузки скетчей из компьютера.
Глава 15. Специальные возможности отдельных плат Arduino 363 Рис. 15.7. Плата LilyPad Arduino 328 Рис. 15.8. Плата LilyPad Arduino USB
364 Часть III. Практическое применение Arduino Рис. 15.9. Плата LilyPad Simple Snap Контакты для FTDI USB Питание датчиков -GND—" Питание датчиков Светодиод . Индикатор ^ •* питания Тестовый светодиод для PIN 13 Рис. 15.10. Контакты и разъемы платы LilyPad Arduino 328
Глава 15. Специальные возможности отдельных плат Arduino 365 Технические характеристики платы LilyPad Arduino 328: ? микроконтроллер: ATmega328V; ? количество цифровых контактов: 14 F из которых могут использоваться как выходы ШИМ); ? количество аналоговых контактов: 6; О рабочее и входное напряжение: 2,7-5,5 В; ? флеш-память: 16 Кбайт B Кбайт используются для загрузчика); ? ОЗУ: 1 Кбайт; Для подключения платы LilyPad Arduino 328 к компьютеру требуется преобразователь USB-Serial (рис. 15.11). Рис. 15.11. Монтажная схема подключения LilyPad Arduino 328 к преобразователю USB-Serial Для загрузки скетчей из Arduino ШЕ на плату LilyPad Arduino 328 необходимо в меню Инструменты выбрать порт подключения платы LilyPad и ее тип — LilyPad Arduino (рис. 15.12). На дешевых переходниках USB-Serial отсутствует контакт DTR, который соединяется с выводом RESET Arduino и сбрасывает микроконтроллер перед загрузкой в него новой программы. Если такого контакта нет, то при загрузке скетча на плату LilyPad Arduino происходит ошибка (рис. 15.13). В таких случаях необходимо после нажатия кнопки Загрузить — когда компиляция скетча завершится и появится надпись Загружаем — нажать и отпустить на плате Arduino кнопку сброса.
366 Часть III. Практическое применение Arduino ; Ф5ЙЛ i\pims,? TfC^tfS ?ЙШ» &&-*ПА1 f ;i;M'>Uib ikrOViUC Г'С О* РГО Г^:; Рис. 15.12. Настройки Arduino IDE для LilyPad Arduino 328 Рис. 15.13. Ошибка при загрузке скетча на плату LilyPad Arduino
Глава 15. Специальные возможности отдельных плат Arduino 367 15.4. Плата Arduino Yun Одной из самых слабых сторон платформы Arduino до недавнего времени являлось отсутствие связи. Плата Arduino Yun решает эту проблему, потому что представляет собой плату Arduino со встроенным модулем Wi-Fi. Кроме того, Yun несет на борту второй микропроцессор, на котором работает облегченная версия Linux с предустановленным языком программирования Python. Кроме Python вы можете также установить Ruby, PHP или Node. О Arduino-часть платы содержит микроконтроллер ATmega32U4, работающий на частоте 16 МГц. «Распиновка» Arduino Yun аналогична Arduino Leonardo, поэтому вместе с Arduino Yun вы можете использовать большинство плат расширения Arduino. ? Linux-часть платы Arduino Yun использует микрокомпьютер Atheros AR9331, работающий под управлением операционной системы Linino — специально подготовленной версии OpenWRT (популярного дистрибутива Linux для встраиваемых систем). На Arduino Yun можно создать какое-либо устройство Интернета вещей или даже «поднять» небольшой сайт и использовать его как главное устройство «умного дома». 15.4.1. Плата расширения Arduino Yun shield Недостатком платы Arduino Yun является ее высокая цена. Эта проблема решается использованием платы расширения Arduino Yun shield (рис. 15.14), которая добавляет любой вашей плате Arduino функционал Linux-части Arduino Yun. В чем отличие «полноценной» Arduino Yun от Arduino Yun shield? И Arduino Yun, и Yun Shield имеют одинаковый процессор, размер памяти и объем оперативной памяти для системы Linux. В принципе, плата расширения Yun Shield, состыкованная Рис. 15.14. Плата расширения Arduino Yun shield, состыкованная с платой Arduino
368 Часть III. Практическое применение Arduino с платой Arduino Leonardo, — это и есть Arduino Yun, но обладание платой Yun Shield даст вам большую гибкость, потому что она может использоваться и с другими платами Arduino: Arduino Uno, Duemilanove и Mega. Рассмотрим работу с платой расширения Arduino Yun shield — состыкуем ее с платой Arduino и подадим питание. Подключиться к плате можно как по Ethernet, так и по Wi-Fi. Плата создает точку доступа Ышпо-лхкххх (рис. 15.15), к которой можно подключиться с компьютера или планшета. Рис. 15.15. Точка доступа платы Arduino Yun shield Эта точка доступа имеет IP-адрес 192.168.240.1. Для входа в веб-интерфейс в браузере набираем адрес: http://192.168.240.1 и на странице авторизации вводим пароль: iduino. Если необходим выход в Интернет, можно подсоединиться к сети Wi-Fi, имеющей доступ в Интернет (рис. 15.16). Рис. 15.16. Настройки подключения к Интернету по Wi-Fi с помощью платы Arduino Yun shield
Глава 15. Специальные возможности отдельных плат Arduino 369 После перезагрузки плата Arduino с установленной на ней платой расширения Arduino Yun shield будет находиться в сети Wi-Fi с доступом в Интернет. Все основные модули шилда Yun: Wi-Fi, Ethernet, USB-хост — подключены к процессору AR9331. Работать с этими устройствами, запускать скрипты и взаимодействовать с различными веб-службами позволяет библиотека Bridge. Существует несколько вспомогательных классов, предназначенных для этого взаимодействия. 15.4.2. Arduino Yun shield: управляем веб-камерой В этом проекте мы подключим к плате Arduino Yun shield веб-камеру и организуем создание снимков при срабатывании датчика движения, подключенного к плате Arduino. Итак, сначала подключим веб-камеру в USB-разъем Arduino Yun shield и соединимся с Arduino Yun shield no ssh (рис. 15.17). Рис. 15.17. Подключение к плате Arduino Yun shield no ssh Теперь выполним в терминале команды: П обновление: opkg update ? установка UVC-драйвера: opkg install kmod-video-uvc ? установка утилиты fswebcam: opkg install fswebcam
370 Часть III. Практическое применение Arduino Для проверки камеры попробуем сделать тестовый снимок (рис. 15.18): fswebcam test.png Затем подключим к плате Arduino датчик движения HC-SR501 (рис. 15.19). ъ I Рис. 15.18. Создание снимка с веб-камеры утилитой fswebcam ife-:^- Рис. 15.19. Монтажная схема подключения к плате Arduino датчика движения HC-SR501 Методы класса Process библиотеки Bridge позволяют плате Arduino запускать на плате Yun shield linux-процессы. И в скетче (листинг 15.7) после обнаружения движения датчиком HC-SR501 мы формируем на основании даты имя файла и запускаем соответствующие linux-процессы.
Глава 15. Специальные еозможности отдельных плат Arduino 371_ Далее с полученными картинками можно делать что угодно — например, отправлять их в какой-либо облачный сервис. // Подключение библиотек Bridge tinclude <Bridge.h> iinclude <Process.h> // process Process picture; // имя файла картинки String filename; // пин подключения HC-SR501 int pin_HCSR501 = 4; // путь сохранения картинки String path = "/root/imgs/"; void setup () { // Bridge Bridge. begin ();' void loop (void) { // датчик сработал if (digitalRead(pin_HCSR501) == true) { // формирование имени файла filename = ""; // запуск процесса в linux (получение даты) picture.runShellCommand("date +%s"); while(picture.running()); // получение данных из linux while (picture.available()>0) { char с = picture.read(); filename += c; } filename.trim (); filename += ".png"; // запуск процесса в linux (получение картинки с камеры) picture.runShellCommand("fswebcam " + path + filename + " -r 640x320") while(picture.running()); delayE000); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i5\_i5_07 сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 16 Взаимодействие Arduino с другими программируемыми системами 16.1. Использование Arduino в проектах LEGO LEGO Mindstorms — конструктор (набор сопрягаемых деталей и электронных блоков) для создания программируемого робота— впервые был представлен компанией LEGO в 1998 г. В 2013 г. вышла 3-я модель серии — LEGO Mindstorms EV3. Наборы LEGO Mindstorms комплектуются стандартными деталями LEGO (балки, оси, колеса, шестерни, сервомоторы), а также сенсорами, двигателями и программируемым блоком. Программируемый блок (рис. 16.1) оснащен процессором Sitara AM 1808 (ARM9) частотой 300 МГц от Texas Instruments, несет на борту 64 Мбайт оперативной памяти, 16 Мбайт флеш-памятй и слот для карт памяти microSDHC объемом до 32 Гбайт. В наличии имеются USB-xoct и модуль связи Bluetooth, возможно подключение к сети Wi-Fi через USB-донгл, поддерживаются устройства Рис. 16.1. Программируемый блок (микрокомпьютер) конструктора LEGO Mindstorm EV3
Глава 16. Взаимодействие Arduino с другими программируемыми системами 373 Apple. Блок оснащен монохромным LCD-дисплеем с разрешением 178x128, а для подключения датчиков на нем имеются 4 порта ввода и 4 порта вывода команд. Тем не менее очень часто в проектах с использованием конструктора LEGO Mindstorm EV3 не хватает возможностей его стандартных датчиков, или для подключения необходимых датчиков недостаточно имеющихся у микрокомпьютера LEGO 4 портов. В такой ситуации допустимо задействовать возможности платформы Arduino. Каждый порт микрокомпьютера LEGO Mindstorm EV3 поддерживает целый ряд различных протоколов — в основном это сделано для совместимости с датчиками NXT и датчиками сторонних производителей. Так, в каждом порту микрокомпьютера имеется канал аналого-цифрового преобразователя и реализована поддержка протоколов 12С и UART. Способность микрокомпьютера LEGO работать по протоколу 12С позволяет подключить к каждому из его портов до 127 подчиненных устройств. И здесь мы рассмотрим способ превратить плату Arduino в 12С-датчик для этого микрокомпьютера. К плате Arduino может быть подключено множество датчиков разных типов, и Arduino будет отправлять данные по протоколу 12С на микрокомпьютер LEGO, выступая в роли своеобразного конвертера: получая запрос (по номеру датчика) из микрокомпьютера LEGO на значение этого датчика и отправляя полученное значение в микрокомпьютер LEGO. 16.1.1. Получение микрокомпьютером LEGO данных с датчика влажности и температуры DHT11, подключенного к плате Arduino Итак, подключим к плате Arduino датчик влажности и температуры DHT11 и выведем его показания на экран микрокомпьютера LEGO. Схема подключения платы Arduino (справа) к порту датчиков микрокомпьютера LEGO (слева) приведена на рис. 16.2 (для лучшего понимания коннектор для подключения датчиков LEGO к порту микрокомпьютера LEGO показан в увеличенном виде). Программное обеспечение для сопряжения микрокомпьютера LEGO и платы Arduino состоит из двух частей: первая часть — программа для среды LEGO Mindstorm EV3, вторая часть — скетч, выполняемый на Arduino. Для создания программы в среде LEGO Mindstorm необходимо скачать блоки Dexter Industries EV3 (Dexter.ev3b) и импортировать их в ПО LEGO Mindstorms EV3 командой меню среды LEGO Mindstorm Инструменты | Мастер импорта блоков. А затем сформировать программу, которая отсылает на плату Arduino по протоколу 12С числа 1 и 2 и выводит приходящие в ответ данные на экран (рис. 16.3). Электронный архив Файл Dexter.ev3b размещен в папке examples\16\ сопровождающего книгу электронного архива (см. приложение 2).
374 Часть Ш. Практическое применение Arduino 6-SDA_Blue 5-SCl_Yel!ow , 4-4 3V_Green 3-GND_Red 2-GND_Black 1-ANA White I I I I i j^5 EV3 — """""" — АО RX ' A1 TX A2 A3 02 A4 *03 A5 04 *05 RES + *06 ViN 07 5V 08 3,3V *D9 AREF *010 GNO *D11 GNO 012 GNO 013 Рис. 16.2. Схема подключения платы Arduino к порту датчиков микрокомпьютера LEGO Рис. 16.3. Программа в среде LEGO Mindstorm EV3 Примечание Изучение программирования в среде LEGO Mindstorm не укладывается в рамки этой книги. В скетче для Arduino (листинг 16.1) плата Arduino, получив запрос от микрокомпьютера LEGO, запрашивает значение либо влажности, либо температуры с датчика DHT11 и отправляет 1 байт данных на микрокомпьютер LEGO. Результат работы объединенной системы «Arduino — LEGO» представлен на рис. 16.4. #include "DHT.h" #include <Wire.h> #define SLAVE_ADDRESS 0x04 tdefine DHTPIN 17 #define DHTTYPE DHT11 // DHT 11 DHT dht(DHTPIN, DHTTYPE); int data; int val,flag=O;
Глава 16. Взаимодействие Arduino с другими программируемыми системами 375 void setup () { Serial.begin(9600); Wire.begin(SLAVE_ADDRESS); Wire.onReceive(receiveData); Wire.onRequest(sendData); dht.begin(); Serial.println("Ready!"); void loopO { if(flag==l) { Serial.print("val=");Serial.println(val); flag=0; if(val==l) data = dht.readHumidity(); else data = dht.readTemperature(); void receiveData(int byteCount) { while(Wire.available()>0) { val=Wire.read(); flag=l; // callback for sending data void sendData () { Wire.write(data); Рис. 16.4. Результат работы объединенной системы «Arduino — LEGO»
376 Часть III. Практическое применение Arduino Электронный архив Полный вариант рассмотренного скетча находится в папке examples\16\_16_01 сопровождающего книгу электронного архива (см. приложение 2). 16.2. Arduino в проектах ROS ROS (Robot Operating System, операционная система для роботов) — это структура программной системы (фреймворк), предоставляющая функционал для распределенной работы по программированию роботов. Система ROS (под названием Switchyard) была первоначально разработана в 2007 г. в Лаборатории искусственного интеллекта Стэнфордского университета. При разработке робота обычно приходится реализовывать свою архитектуру, свой протокол обмена сообщениями, драйвер пульта управления, логику навигации и пр. И даже если имеется возможность использовать для этих задач различные готовые библиотеки, то все равно остается серьезная проблема — объединить их для робота в единую систему. Разработчики ROS позиционируют свою систему именно как операционную — для программ взаимодействия и управления роботом ROS играет роль операционной системы, предоставляя программам управления свои интерфейсы, библиотеки и готовые приложения. ROS работает под уже готовой ОС (Ubuntu Linux), в которой реализует свой дополнительный слой абстракции— конкретно для управления роботами. ROS обеспечивает стандартные службы операционной системы, такие как аппаратная абстракция, низкоуровневый контроль устройств, реализация часто используемых функций, передача сообщений между процессами и управление пакетами. ROS развивается в двух направлениях: в качестве уже описанной здесь операционной системы и в виде поддерживаемых пользователями пакетов (ros-pkg), организованных в наборы (стеки), реализующие различные функции робототехники. Так, ROS содержит вспомогательные библиотеки и приложения для роботов: преобразование систем координат, утилиты для визуализации данных и распознавания объектов, стек навигации и многое другое. Реализованы для ROS и драйверы, позволяющие единым образом работать со многими устройствами: джойстиками, устройствами GPS, камерами, лазерными дальномерами и пр. ROS основана на архитектуре графов, где обработка данных происходит в узлах, которые могут получать и передавать между собой сообщения (структурированные данные). Комбинируя готовые узлы ROS и по необходимости дописывая собственные, можно существенно сократить время разработки и позволить себе сконцентрироваться только на тех задачах, которые действительно нужно решить. К настоящему времени под управлением ROS работает уже много роботов. Вот неполный их список: PR2, TurtleBot, PR1, HERB, STAIR I и II, Nao, Husky A200, iRobot Create, LEGO Mindstorms NXT. ROS выпускается в соответствии с условиями лицензии BSD и с открытым исходным кодом. Она бесплатна для использования как в исследовательских, так и в
Глава 16. Взаимодействие Arduino с другими программируемыми системами 377 коммерческих целях. Пакеты из ros-pkg распространяются на условиях различных открытых лицензий. 16.2.1. Установка ROS Шаги по установке ROS Kinetic Kame под Ubuntu Linux расписаны на официальном сайте системы: http://www.ros.org/wiki/fuerte^nstallation/Ubuntu. Рассмотрим ее установку на компьютер с операционной системой Ubuntu Xenial A6.04 LTS). 1. Добавляем адрес сервера ROS, чтобы менеджер пакетов знал, откуда брать пакеты ROS: sudo sh -с 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list1 2. Получаем ключ: sudo apt-key adv —keyserver fhkp: //keyserver.ubuntu.com: 80' —recv-ключ C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 3. Обновляем список пакетов — тем самым сервер ROS.org будет проиндексирован: sudo apt-get update 4. Отдаем команду установки ROS Fuerte (рекомендованная конфигурация Desktop-Full): sudo apt-get install ros-kinetic-desktop-full Разработчики ROS стремятся интегрировать в систему лучшие открытые робото- технические библиотеки, сохраняя при этом модульность системы, чтобы пользователь мог установить только те модули, которые ему действительно необходимы. Некоторые библиотеки вынесены из ROS и устанавливаются в ОС стандартным образом, что позволяет использовать эти библиотеки и без ROS: 1. Установим необходимый нам пакет rosseriai: sudo apt-get install ros-kinetic-ros-comm 2. Отдельно необходимо установить также пакеты rosinstaii и rosdep: sudo apt-get install python-rosinstaii python-rosdep 3. В начале новой сессии bash необходимо прописать установку переменных окружения ROS: echo "source /opt/ros/kinetic/setup.bash" » ~/.bashrc . ~/.bashrc На этом установка ROS завершена. 16.2.2. Узлы и темы в ROS Узел— это исполняемый файл пакета ROS. Узлы ROS используют клиентские библиотеки ROS для связи с другими узлами. Клиентские библиотеки ROS позволяют реализовывать узлы ROS на различных языках программирования, например:
378 Часть III. Практическое применение Arduino ? Rospy — клиентская библиотека для Python; ? Roscpp — клиентская библиотека для C++; ? Rosjava — клиентская библиотека для Java. Узлы могут публиковать сообщения по теме (publisher), а также подписаться на тему для приема сообщений (subscriber). Сообщения — тип данных, используемых для публикации или подписки на тему. Типы сообщений описываются в файлах сообщений msg — простых текстовых файлах с полем типа и полем имени в строке. Вот список типов полей, которые можно использовать: int8, intl6, int32, int64, float32, float64, string, time, duration, other msg files, variable-length array[], fixed-length array[C]. Файлы msg служат для генерации исходного кода для сообщений на разных языках и хранятся в подкаталоге msg каталога пакета. Узлы могут также предоставлять или использовать службы (Service). Службы позволяют узлам послать запрос и получить ответ. Файлы служб srv — такие же простые текстовые файлы, как и файлы msg, но они состоят из двух частей: запроса и ответа. Эти две части, разделяются линией: —. Вот пример файла srv: int64 A int64 В int64 Sum Здесь айв — запрос, a sum — ответ. Файлы srv хранятся в подкаталоге srv каталога пакета. 16.2.3. Пакет rosserial Библиотека rosserial устанавливает соединение точка-точка (point-to-point connection) через последовательный порт с недорогими контроллерами (типа Arduino) так, что вы можете посылать сообщения ROS туда и обратно. Библиотека rosserial состоит из общего Р2Р-протокола, библиотеки для работы с Arduino и узлов для ПК. Библиотека rosserial для работы с Arduino находится в каталоге проекта serial (в каталоге serial_arduino\libraries). Для работы нам понадобится и библиотека roslib, поэтому копируем каталог rosjib в библиотечный каталог libraries Arduino ШЕ (рис. 16.5). Электронный архив Библиотека rosjib размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
Глава 16. Взаимодействие Arduino с другими программируемыми системами 379 Рис. 16.5. Подключение библиотеки rosjib 16.2.4. Подготовка сообщения (publisher) на Arduino Создадим скетч для Arduino, демонстрирующий создание узла ROS, публикующего сообщения в тему. Для этого соединим плату Arduino с подключенным к ней датчиком температуры DS18B20 по последовательному порту (в рассматриваемом случае это порт /dev/ttyusBO) с компьютером, на котором запущена ROS, и будем отправлять в ROS значения температуры с датчика, используя библиотеки OneWire и roslib. Работу с датчиком температуры, работающим по протоколу 1-Wire, мы подробно рассмотрели в предыдущих главах (см. например, главы 11 и 72), поэтому здесь остановимся на работе библиотеки ros lib. В каждую программу ROS для Arduino необходимо включить заголовочный файл ros.h и файлы заголовков для всех типов сообщений, которые мы будем использовать, — в нашем случае это std_msgs/Float32.h: #include <ros.h> tinclude <std_msgs/Float32.h> Далее нам необходимо создать экземпляр объекта узла seriainode, что позволит нашей программе выступать в качестве подписчика (subscriber) либо публиковать сообщения (publisher): ros::NodeHandle nh; Создаем экземпляр publisher для нашего узла seriainode, публикующий сообщения типа stdjnsgs: :Float32 В тему temperature: stdjnsgs::Float32 float32_msg; ros::Publisher chatter("temperature", &float32_msg);
380 Часть III. Практическое применение Arduino В подпрограмме setup о необходимо инициализировать узел и объявить о роли узла chatter В качестве publisher: nh.initNode (); nh.advertise(chatter); В цикле loop () после считывания данных с датчика температуры публикуем сообщение в тему и вызываем ros:: spinOnce (), где обрабатываются все функции обратного вызова соединения. chatter.publish( &float32_msg ); nh.spinOnce(); Код этого скетча представлен в листинге 16.2. #include <OneWire.h> OneWire dsA0); // линия 1-Wire будет на pin 10 #include <ros.h> tinclude <std_msgs/Float32.h> ros::NodeHandle nh; std_msgs::Float32 float32_msg; ros::Publisher chatter("temperature", &fIoat32_msg); void setup(void) { nh.initNode(); nh.advertise(chatter); } void loop(void) { byte i; byte present =0; byte data[12]; byte addr[8]; if ( Ids.search(addr)) { ds.reset_search(); return; } ds.reset(); ds.select(addr); ds.write@x44,1); // запускаем конвертацию delayA000); // скорее всего, достаточно 750 ms present = ds.reset(); ds.select(addr); ds.write(OxBE); // считываем ОЗУ датчика J
Глава 16. Взаимодействие Arduino с другими программируемыми системами 381 for ( i - 0; i < 9; i++) { // обрабатываем 9 байтов data[i] = ds.read(); } Serial.print(" CRC="); Serial.print( OneWire::crc8( data, 8), HEX); Serial.println(); // высчитываем температуру int HighByte, LowByte, Temp; float Tempfl,Tempf2; LowByte = data[0]; HighByte = data[l]; Temp = (HighByte « 8) + LowByte; Tempfl=Temp/16; Tempf2=(Temp%16)*100/16; float32_msg.data=Tempfl+Tempf2/100; // публикуем сообщение chatter.publish( &float32_msg ); nh.spinOnce(); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\16\_16_02 сопровождающего книгу электронного архива (см. приложение 2). Теперь проверим работу этого скетча. Первое, что необходимо выполнить, — это команду roscore. Команда rosnode отображает информацию об узлах ROS, которые работают в настоящий момент. Команда rosnode list выдает список этих активных узлов. В терминале увидим: /rosout Соответственно, есть только один работающий узел: rosout. Этот узел работает всегда, т. к. он собирает и логирует отладочные сообщения узлов. Команда rosrun позволяет назначить имя пакета, чтобы непосредственно запустить его узел: $ rosrun [package_name] [node_name] Запускаем узел seriainode.py пакета rosseriai^python, который соединяет нашу Arduino с остальной частью. ROS. Необходимо выставить используемый последовательный порт: rosrun rosserial_python serial_node.py /dev/ttyUSBO В терминале набираем: $ rosnode list И смотрим список активных узлов: /rosout /serial node
382 Часть III. Практическое применение Arduino Утилита rxgraph, являющаяся частью пакета rxtoois, позволяет визуально показать узлы и темы, запущенные в настоящий момент. Набираем в терминале: $ rxgraph Результат показан на рис. 16.6. Рис. 16.6. Утилита rxgraph демонстрирует список активных узлов и тем Утилита rostopic позволяет получить информацию о темах ROS. Команда rostopic echo показывает данные, опубликованные в теме. Набираем в терминале: $ rostopic echo /temperature и видим постоянно поступающие с Arduino данные датчика температуры (рис. 16.7). Рис. 16.7. Публикация сообщений из Arduino в тему temperature
Глава 16. Взаимодействие Arduino с другими программируемыми системами 383 16.2.5. Создание подписки (subscriber) на Arduino Теперь рассмотрим пример использования Arduino в качестве узла subscriber для приема сообщений. В э^ом примере мы будем зажигать/гасить светодиод, подключенный к выводу 13 Arduino, получая сообщения из ROS. Включаем заголовочный файл ros.h и файл заголовков для всех типов используемых сообщений, — в нашем случае это std_msgs/Empty.h (для пустых сообщений): #include <ros.h> #include <std_msgs/Empty.h> Далее необходимо создать экземпляр объекта узла serial jiode, что позволит нашей программе выступать в качестве подписчика (subscriber) либо публиковать сообщения (publisher): ros:: NodeHandle nh; Создаем экземпляр subscriber для нашего узла, публикующий пустые сообщения типа stdjnsgs: :Float32 В тему toggle_led: ros::Subscriber<std_msgs::Erapty> sub("toggle_led", &messageCb ); Создаем для нашего узла функцию обратного вызова messagecb. Эта функция должна постоянно получать сообщение в качестве аргумента. Для нашей функции обратного вызова messagecb назначим тип сообщения stdjnsgs::Empty. По получении сообщения функция инвертирует значение сигнала на выводе 13 Arduino, при этом зажигая/гася светодиод. void messageCb( const stdjnsgs::Empty& togglejnsg){ digitalWriteA3, HIGH-digitalReadA3)); // blink the led } В подпрограмме setup о необходимо инициализировать узел и объявить о роли узла в качестве подписчика на сообщения: nh.initNode (); nh. subscribe (sub) ; И наконец, в цикле loop () вызываем ros:: spinOnce (), где обрабатываются все функции обратного вызова соединения: nh. spinOnce () ; Код этого скетча представлен в листинге 16.3. tinclude <ros.h> Hnclude <stdjnsgs/Empty.h> ros::NodeHandle nh; void messageCb( const stdjnsgs::Empty& togglejnsg){ digitalWriteA3, HIGH-digitalReadA3)); // blink the led
384 Часть III. Практическое применение Arduino ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb ); void setup() { pinModeA3, OUTPUT); nh.initNode(); nh.subscribe(sub); } void loop() { nh.spinOnce(); delayA); } Запускаем узел serial node, py пакета rosseriai_python, который соединяет нашу Arduino с остальной частью ROS. Необходимо выставить задействованный последовательный порт (здесь использована другая плата Arduino, подключенная к порту ttyACMO): rosrun rosserial_python serial_node.py /dev/ttyACMO Переходим на компьютер с запущенной ROS и проверяем список активных узлов: $ rosnode list Смотрим результат: /rosout /serial_node Наш узел запущен как подписчик на сообщения по теме toggieied, но никаких сообщений он пока не получил. Связь по темам осуществляется путем отправки сообщений ROS между узлами. Для общения издатель (publisher) и абонент (subscriber) должны отправлять и получать сообщения одинакового типа. Это означает, что тип темы определяется типом сообщений, которые в ней публикуются. Тип сообщения, отправляемого в тему, может быть определен с помощью команды rostopic type: $ rostopic type toggle_led Результат: std_msgs/Empty Теперь используем rostopic с сообщениями — rostopic pub публикует данные в тему: rostopic pub [topic] [msg_type] [args] Отправим единичное сообщение: rostopic pub toggle_led std_msgs/Empty —once Светодиод должен изменить значение на противоположное.
Глава 16. Взаимодействие Arduino с другими программируемыми системами 385^ Для отправки сообщения в цикле (-г) с определенной частотой введем команду: rostopic pub toggle_led std_msgs/Empty -r 1 Эта команда публикует сообщение с частотой 1 Гц в тему toggie_ied. Светодиод будет мигать с частотой 2 раза в секунду. Электронный архив Полный вариант рассмотренного скетча находится в папке examplesW6W6_03 сопровождающего книгу электронного архива (см. приложение 2). 16.2.6. Связь через ROS двух плат Arduino Теперь создадим пример передачи сообщений через ROS между двумя платами Arduino. На одной плате Arduino, соединенной с ROS, находится датчик температуры DS18B20 (см. разд. 16.2.4). Подключим к ROS по другому последовательному порту вторую плату Arduino, к которой подключен дисплей WH0802 — на него мы и будем выводить показания температуры с датчика, расположенного на первой плате Arduino. Скетч для публикации показаний температуры у нас уже есть. Скетч для получения сообщений с показаниями температуры, публикуемыми в тему temperature, представлен в листинге 16.4. // подключить библиотеку LiquidCrystal tinclude <LiquidCrystal.h> // создание экземпляра объекта LiquidCrystal LiquidCrystal ledA2, 11, 5, 4, 3, 2); // rosserial tinclude <ros.h> tinclude <std_msgs/Empty.h> tinclude <stdjnsgs/Float32.h> ros::NodeHandle nh; void messageCb( const stdjcnsgs:: Float32& toggle_msg) { digitalWriteA3r HIGH-digitalReadA3)); // blink the led led.setCursor@, 0); led.print("Temp="); led.setCursor@, 1); led.print(togglejmsg.data);} ros:: Subscriber<std_msgs:: Float32> sub ("temperature11, SmessageCb ); void setup.() { led.begin(8, 2); pinModeA3, OUTPUT); nh.initNode(); nh.subscribe(sub);
386 Часть III. Практическое применение Arduino void loop () { nh.spinOnce(); delayA000); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\16\_16JL сопровождающего книгу электронного архива (см. приложение 2). Теперь посмотрим, как реализовать в ROS передачу. Запустить два узла serial__node с одним именем не получится. Но одна из особенностей ROS состоит в том, что вы можете переназначить имена (Names) узлов из командной строки: $ rosrun rosserial_python serial_node.py /dev/ttyUSBO name:=seriall $ rosrun rosserial_python serial_node.py /dev/ttyACMO name:=serial2 Посмотрим список активных узлов командой rosnodeiist: /rosout /seriall /serial2 А командой rxgraph посмотрим узлы и темы (рис. 16.8). При этом показания температуры отображаются на дисплее WH0802. Рис. 16.8. Утилита rxgraph — список активных узлов и тем 16.3. Arduino и Raspberry pi Arduino и Raspberry Pi — совершенно разные устройства. Raspberry Pi — это аппаратная платформа. Arduino — микрокомпьютер. Они не являются конкурентами и служат для выполнения разных задач.
Гпава 16. Взаимодействие Arduino с другими программируемыми системами 387 Arduino идеален для аппаратных проектов, которые основаны на считывании информации с различных датчиков и чипов и совершении простых действий в качестве реакции на полученные сигналы. Raspberry Pi можно использовать с той же целью, но простота выполняемых процессов не оправдывает мощность и сложность применяемой системы. В то же время мощность Arduino не позволяет производить сложные вычислительные операции, а работа с Интернетом требует подключения дополнительных модулей и написания программ для них. Поэтому оптимальным выбором является использование обоих устройств в тандеме — на Arduino могут выполняться простые операции, a Raspberry Pi позволяет контролировать процессы на одном или нескольких Arduino и легко взаимодействовать с Интернетом. Существует Немало вариантов соединения Arduino и Raspberry Pi, обеспечивающих вам клиентский доступ к настройкам и коду через Pi, в то время как Arduino контролирует управление рабочими органами и собирает информацию с сенсоров: по USB, по локальной сети или в виде простого подключения портов ввода/вывода Arduino к Raspberry Pi. Мы рассмотрим здесь вариант соединения по последовательному порту. 16.3.1. Установка WeblOPi на Raspberry Pi На стороне Raspberry Pi мы воспользуемся фреймворком WeblOPi, позволяющим контролировать состояние и управлять всеми портами GPIO локально или удаленно из браузера или любого приложения. Возможности WeblOPi: ? REST API через HTTP и СоАР с поддержкой мультикаста; П работа с GPIO, Serial, I2C, SPI, 1-Wire; ? встроенная поддержка более чем 30 устройств, включая ЦАП, АЦП, датчики; ? совместимость с Python 2 и 3; ? защита логином/паролем; Я множество примеров. Чтобы установить фреймворк WeblOPi на Raspbian, его необходимо сначала скачать, а затем извлечь. Следующий сценарий установки автоматически загрузит и инсталлирует необходимые зависимости: wget http://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz tar xvzf WebIOPi-0.7.1.tar.gz cd WebIOPi-0.7.1 wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi- pi2bplus. patch patch -pi -i webiopi-pi2bplus.patch sudo ./setup.sh
Звв . Часть III. Практическое применение Arduino После достаточно продолжительной установки WeblOPi будет готов к запуску. Лучше запускать его как сервис: sudo /etc/init.d/webiopi start Теперь можно открыть веб-браузер на любом компьютере в домашней сети и набрать адрес: http://iprasp:8000, где iprasp — ПР-адрес Raspberry Pi. Имя пользователя — webiopi, пароль — raspberry. В результате в браузере откроется стартовая страница WeblOPi (рис. 16.9). Перейдя по ссылке GPIO Header, вы попадете на страницу управления выводами GRI0 (рис. 6.10), где можно задать режим работы любого вывода и установить значение на выходе. WeblOPi Main Menu GPIO Header Coi^aiidDetagtteRaspbercy Pi GPIO wife ^ GPIO List Control and Debug tfae Raspberry К GPIO ordered n a siogje column. Serial Monitor Use die browser to play wife Serial intofeces configured io WebfOPL Devices Monitor Control and Debug devices and circuits wired to your Pi and configured ki WeblOPi. Рис. 16.9. Стартовая страница WeblOPi WeblOPi включает в себя HTTP-сервер, обеспечивающий ресурсы HTML, и интерфейс REST API для управления выводами GRIO. Браузер сначала загружает HTML- файл с включенной Javascript библиотекой webiopi.js, включающей jquery для асинхронных вызовов к REST API. Этот метод очень эффективен, потому что не требует для обновления данных обновления страницы. Настроить сервер WeblOPi можно путем редактирования его файла конфигурации /etc/webiopi/config: sudo nano /etc/webiopi/config В файле конфигурации сервера WeblOPi нас интересуют: О блок [нттр], который позволяет включить или отключить HTTP, а также изменить значение порта. Здесь же можно изменить местоположение файла passwd, домашней папки и название индексного файла HTML:
Глава 16. Взаимодействие Arduino с другими программируемыми системами 389 О 192.168A101:8000/app/gpJo-header 3JV GPIO2 G«O3 СРЮ 4 GROUND GPTO17 GHO27 GPIO22 3.3V GPIO10 GPIO9 СЯРЮ11 GROUND 5.0V GROUND UARTTX tJARTRX СРЮ18 GROUND СРЮ 23 GPIO24 GROUND GP1O25 €ЯЮ9 GPK> 7 Рис. 16.10. Страница управления выводами GRIO [HTTP] ' enabled = true port = 8000 passwd-file = /etc/webiopi/passwd doc-root = /home/pi/webiopi/examples/scripts/macros welcome-file = index.html D а также блок [scripts] , который определяет список скриптов, выполняемых при запуске WeblOPi. Например: [SCRIPTS] myscript = /home/pi/webiopi/examples/scripts/macros/script.py 16.3.2. Обмен данными по последовательному порту Соединим Arduino и Raspberry Pi по последовательному порту, для чего на Arduino Mega воспользуемся портом Serial 1. Подключение Arduino к LFSB-порту Raspberry Pi выполняется через переходник USB-UART (рис. 16.11). Пусть наша связка «Arduino — Raspberry P»i установлена на платформе робота. В этом примере мы будем отправлять на Raspberry Pi данные с подключенных к плате Arduino Mega датчика GPS (широту и долготу) и фоторезистора. Формат отправки данных: ? о=хх@ — данные фоторезистора @-100); ? з=ууууууу@ — данные lat GPS (широта); ? 4=zzzzzzzz@ — данные Ion GPS (долгота). Получение данных с Raspberry Pi: ? l;ххх;ххх$ — движение вперед со скоростью ххх @-255); ? 2; ххх;ххх $ — движение назад со скоростью ххх @-255);
390 Часть III. Практическое применение Arduino Рис. 16.11. Монтажная схема соединений Arduino Mega и Raspberry Pi ? 3; ххх; ххх $ — остановка; ? 4; ххх; ххх $ — движение в л ево; ? 5; ххх;ххх$ — движение вправо; ? 11; ххх; ххх$ — подсветка светодиодов в зависимости от освещенности; ? 12; ххх;ххх$ — зажечь/погасить светодиоды. Схема соединения всех элементов показана на рис. 6.12. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\16\_16jO5 сопровождающего книгу электронного архива (см. приложение 2). 16.3.3. Управление движущейся платформой на базе Arduino по web-интерфейсу Raspberry Pi Сначала настроим в. файле конфигурации config, который находится в папке /etc/webiopi/, необходимые параметры: путь к домашней папке и путь к скрипту camboti .py, выполняемому при запуске WeblOPi (мы напишем его чуть позже):
Глава 16. Взаимодействие Arduino с другими программируемыми системами 391 I i О.
392 Часть III. Практическое применение Arduino doc-root = /home/pi/webiopi/examples/malina script = /home/pi/webiopi/examples/malina/cambotl.py Теперь напишем на python серверный скрипт camboti.py, который будет выполняться при запуске WeblOPi. Этот скрипт запускает прослушку по последовательному порту и исполнение макросов на веб-странице. Содержимое скрипта показано в листинге 16.5. # Imports import webiopi from webiopi.devices.serial import Serial import sys from subprocess import call import time serial = Serial("ttyUSBO", 9600) sensors = [0 for a in rangeF)] # Retrieve GPIO lib GPIO = webiopi.GPIO I # # Macro definition part # : л. @webiopi.macro def go_forward(): serial.writeString("l;255;255$") @webiopi.macro def go_backward(): serial.writeString(;255;255$") 0webiopi.macro def turn_left(): serial. writeString (" 4; 2*55; 0$ ") @webiopi.macro def turn_right(): serial.writeString(;0;255$") @webiopi.macro def stop(): serial.writeString(;0;0$") Gwebiopi.macro def getSensor(channel): percent = sensors[int(channel)] return "%.2f" % percent
Глава 16. Взаимодействие Arduino с другими программируемыми системами 393 @webiopi .macro def headlighton() : serial.writeString(2;1;1$") @webiopi. macro def headlightoff () : serial.writeString(2;0;0$") @webiopi .macro def autoheadlighton(): serial.writeString(1;1;1$") @webiopi.macro def autoheadlightoff(): serial.writeString(1;0;0$") def loop () : if (serial.available() > 0): data = serial.readString() lines = data.split("@") count = len(lines) lines = lines[0:count-1] for pair in lines: cv = pair.split("=") channel = int(cv[0]) value = int(cv[l]) sensors[channel] = value webiopi.sleepA) # при загрузке WebIOPi def setup(): stop() webiopi.sleepE) Электронный архив Файл скрипта cambottpy находится в папке examples\16\ сопровождающего книгу электронного архива (см. приложение 2), В файле конфигурации config настроим путь к HTML-файлу, выполняемому при запуске WeblOPi: welcome-file = indexl.html Открываемая веб-страница содержит кнопки управления движущейся платформой и поля для отображения данных, поступающих с нее (данные фоторезистора и датчика GPS).
394 Часть III. Практическое применение Arduino Содержимое HTML-файла показано в листинге 16.6. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv = "cache-control" content = "max-age=0"> <meta http-equiv = "cache-control" content = "no-cache"> <meta http-equiv = "expires" content = "> <meta http-equiv = "expires" content = "Tue, 01 Jan 1970 1:00:00 GMT"> <meta http-equiv = "pragma" content = "no-cache"> <meta name="viewport" content = "height = device-height, width = 420, user-scalable = no" /> <title>CamBot</title> <script src="jquery-3.3.1 .min. js"x/script> <script type="text/javascript" src="/webiopi.js"x/script> <script type="text/javascript"> function init() { var button; button = webiopi().createButton("bt_up", "/\\", go_forward); $("#up").append(button); button = webiopi().createButton("bt_leftf\ "<", turn_left); $("#middle").append(button); button = webiopi().createButton("bt_stop", "v"f stop); $("#middle").append(button); button = webiopi(),createButton("bt_right", ">", turn_right); $("#middle").append(button); button = webiopi().createButton("bt_down", "\\/", go_backward) ; $("#down").append(button); setlnterval(updateUI, 1000); setlnterval(koordToServer, 30000); function go_forward() { webiopi().callMacro("go_forward");
Глава 16. Взаимодействие Arduino с другими программируемыми системами 395 function go_backward() { webiopi().callMacro("go_backward"); function turn_right() { webiopi().callMacro("turn_right"); function turn_left() { webiopi().callMacro("turn_left"); function stopO { webiopi().callMacro("stop"); function hi() { var pi; if(document.getElementByld("headlight").checked) {webiopi().callMacro("headlighton");pl=l;} else {webiopi().callMacro("headlightoff");pl=0;} function autohl() { if(document.getElementByld("autoheadlight").checked) {webiopi().callMacro("autoheadlighton");} else {webiopi().callMacro("autoheadlightoff");} function koordToServer() { $.ajax({ type: 'GET1, url: f http://xxxxxxx.ru/department/getKoords.php', cache: false, data: {"bot": 1, "lat": document.getElementByld(flsensor3").innerText, "Ion": document.getElementByld("sensor4").innerText}, success: function (data) { ;//alert(data);
396 Часть III. Практическое применение Arduino function updateUI() { // call getSensor macro«for Arduino analog channel 0-3 webiopi().callMacro("getSensor", 0, sensorCallback); webiopi().callMacro("getSensor", 1, sensorCallback); webiopi().callMacro("getSensor", 2, sensorCallback); webiopi().callMacro("getSensor", 3, sensorCallback); webiopi().callMacro("getSensor", 4, sensorCallback); // callback function used to display sensor data function sensorCallback(macroName, channel, data) { // use jQuery to change spans content if(channel<3) {$("#sensor"+channel).text(data);} else {$("#sensor"+channel).text(data/1000000);} webiopi().ready(init); function get__img() { document.getElementById(",imglft).src="http://192.168.0.100:8000/img.jpg"; </script> <style type="text/css"> button { margin: 5px 5px 5px 5px; width: 50px; height: 50px; font-size: 24pt; font-weight: bold; color: black; } </style> </head> <body> <div id="content" align="center"> <img width=20" height=40" src="http://192.168.0.100:9090/?action=stream"> <div id="up"x/div> <div id="middle"x/div> <div id="down"x/div>
Глава 16. Взаимодействие Arduino с другими программируемыми системами 397 <div align="center"> <div>Lighting: <span id="sensorO"x/span> <div>Sensor center: <span id="sensorl"x/span> cm</div> <div>Sensor right: <span id="sensor2"></span> on</div> <div>GPS lat: <span id="sensor3"></span> <div>GPS Ion: <span id=lfsensor4"x/span> <form id=llforml"> </form> <div align="center"> Headlights<input type^'checkbox" id="headlight" onchange="hl();"><br> auto headlights<input type=:"checkbox" id="autoheadlight" onchange="autohl();"> </body> </html> Вид страницы показан на рис. 16.13. Электронный архив Файл index1.html находится в папке examp!es\16\ сопровождающего книгу электронного архива (см. приложение 2). Lighting: 56 % Sensor center: cm Sensor right: cm GPS lat: 43.018442 GPS Ion: 44Л37259 Headlights 0 auto headlights ? Рис. 16.13. Вид веб-страницы для управления движущейся платформой из WeblOPi
ГЛАВА 17 Программирование в среде Arduino IDE других плат Начиная с версии 1.6.5, Arduino IDE предоставила официальную поддержку для добавления сторонних плат. Это позволяет программировать в знакомой среде и на понятном Arduino языке Arduino-несовместимые платы и соответственно использовать их в своих проектах. 17.1. ESP8266 — микроконтроллер с интерфейсом Wi-Fi С конца 2014 г. на китайских торговых площадках появились модули Wi-Fi ESP8266. Причем, как выяснилось, это не просто модули Wi-Fi, а полноценные 32-битные микроконтроллеры со своими наборами GPIO, в том числе поддерживающими шины SPI, UART и 12С. При этом сами модули состоят из минимального количества деталей: собственно микросхемы ESP8266, флеш-памяти и кварцевого генератора. Характеристики этих модулей представлены в табл. 17.1. В настоящее время выпускается более 12 модификаций плат модулей ESP8266, различающихся количеством выводов и вариантами исполнения. Модули продаются с загруженной прошивкой, которая образует мост Wi-Fi —> UART для подключения к другому микроконтроллеру, в том числе и к Arduino. Настройка соединения и обмен данными осуществляются с помощью АТ-команд. Возможны два варианта работы с модулем ESP8266: О использование его совместно с платой Arduino, которая- будет управлять модулем по UART; ? создание собственной прошивки для модуля ESP8266 и его применение как самодостаточного устройства. Таблица 17.1. Характеристики модулей ESP8266 Частота Стандарт Мощность Wi-Fi 2412-2484 МГц 802.11 b/g/n + 20дБ
Глава 17. Программирование в среде Arduino IDE других плат 399 Таблица 17.1 (окончание) Поддерживаемые типы шифрования Поддерживаемые режимы работы Напряжение питания Потребление тока Количество доступных выводов GPIO Внешняя флеш-память RAM данных RAM инструкций Температурный режим WEP, WPA, WPA2 Клиент (STA), точка доступа (АР), клиент + точка доступа (STA + АР) 1,7-3,6 В 70 мА (пиковое значение 240 мА) 4-10 512 Кбайт 80 Кбайт 32 Кбайт От-40до70°С Один из недостатков плат ESP8266— малое количество контактов, что сильно ограничивает их применение в больших проектах. Эта проблема решена в новой линейке контроллеров ESP— ESP32, которые отличаются большей производительностью, большим объемом оперативной памяти, имеют поддержку не только Wi-Fi, но и Bluetooth, а также несут на борту большее количество выводов. Подробно ознакомиться с описанием микроконтроллера ESP32 можно,, например, по этой ссылке: http://micpic.ru/home/proekty-na-esp32/194-opisanie-mikrokontrollera- esp32.html. 17.1.1. Установка Arduino IDE для работы с ESP8266 Arduino IDE позволяет создавать для ESP8266 прошивки и прошивать их в ESP8266 точно так же, как вы это делаете с Arduino. К тому же большая часть библиотек для Arduino, не использующих внутренние порты и прочие аппаратные возможности плат Arduino, после небольшой доработки отлично работают и на ESP-модулях. В настоящее время для использования с ESP8266 адаптировано уже достаточно много библиотек. Рассмотрим установку Arduino ШЕ для работы с ESP8266. Сначала необходимо скачать с официального сайта Arduino среду разработки Arduino IDE версии не ниже 1.6.5 и установить ее на компьютер (см. главу J). Затем запускаем Arduino IDE, выполняем команду меню Файл | Настройки (рис. 17.1) и в поле Additional Boards Manager URLs вводим: http://arduino.esp8266.com/stable/package_esp8266com_index.json Нажимаем кнопку ОК. Теперь выполняем команду меню Инструменты | Плата | BoardsManager и в списке ищем плату ESP8266. Выбираем этот пункт и версию и нажимаем на кнопку Install (рис. 17.2)— запустится процесс скачивания и установки Arduino IDE для ESP8266 (рис. 17.3).
400 Часть III. Практическое применение Arduino ора: -С|*с:?-«|-шае &aife Code гФФи Рис. 17.1. Добавление в поле Additional Boards Manager URLs адреса для скачивания Arduino IDE для работы с ESP8266 Рис. 17.2. Выбор платы ESP8266 в окне Boards Manager
Глава 17. Программирование в среде Arduino IDE других плат 401 Рис. 17.3. Процесс скачивания и установки Arduino IDE для ESP8266 По завершении этого процесса рядом с наименованием платы ESP8266 возникнет надпись INSTALLED (рис. 17.4), а в списке плат, открываемом по команде меню Инструменты | Плата, появятся платы ESP8266 (рис. 17.5). :i; п-Л^Г Рис. 17.4. Arduino IDE для ESP8266 установлена
402 Часть III. Практическое применение Arduino Рис. 17.5. Выбор плат ESP8266 в Arduino IDE для ESP8266 17.1.2. Печать курса валют на термопринтере в проекте Интернета вещей Наличие у модулей ESP8266 интерфейса Wi-Fi позволяет использовать их в проектах Интернета вещей (IoT). Создадим на ESP8266 проект IoT-принтера, который будет печатать курс валют на текущую дату, получая через Интернет данные с сайта cbr.ru. В качестве принтера в этом проекте мы воспользуемся бюджетным термопринтером, выпускаемым специально для Arduino (рис. 17.6). Принтер использует термобумагу 2,25 дюйма, которую можно приобрести в магазине канцелярских товаров. Вам также понадобится регулируемый источник питания от 5 до 9 В постоянного тока, который может обеспечить ток более 1,5 А. Общение с принтера с платой Arduino мы организуем с помощью UART-соеди- нения.
Глава 17. Программирование в среде Arduino IDE других плат 403 Рис. 17.6. Термопринтер для Arduino Прежде всего, необходимо провести начальный тест принтера. Подключите принтер к блоку питания, держа нажатой кнопку на его верхней панели, — будет распечатана таблица шрифтов и некоторая дополнительная информация (рис. 17.7). Нужный нам параметр — скорость обмена по последовательному порту (Baudrate): 19 200 бод. Рис. 17.7. Распечатка тестовой страницы Подключим теперь термопринтер к модулю ESP8266 (в проекте использована отладочная плата NodeMCU — удобная платформа на основе модуля ESP8266) по схеме, представленной на рис. 17.8, и загрузим на плату NodeMCU код из листинга 17.1 (для программирования нам потребуется Arduino-библиотека для принтера
404 Часть III. Практическое применение Arduino Adafruit Thermal). В результате мы увидим вывод на принтер тестовых данных (рис. 17.9). Электронный архив Библиотека Adafruit Thermal размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2), + 5V2A Рис. 17.8. Монтажная схема подключения термопринтера к отладочной плате NodeMCU ESP8266 Рис. 17.9. Вывод тестовых данных из скетча на принтер
Глава 17. Программирование в среде Arduino IDE других плат 405 tinclude "AdafruitJThermal.h" AdafruitJThermal printer(&Serial); // Pass addr to printer constructor void setup() { // запуск последовательного порта Serial.beginA9200); // инициализация принтера printer.begin(); delayC000); // настройки по умолчанию printer.wake(); printer.setDefault(); printer.printIn(); printer.println(); delay(lOOO); printer.println("test "); printer.println(flesp8266 iot") ; printer.printIn("thermal printer"); } void loop() { Нашему проекту для правильной работы необходимо знать реальное время, для получения которого мы воспользуемся модулем часов реального времени (RTC) на микросхеме DS3231. Подключение модуля DS3231 к модулю NodeMCU ESP8266 осуществляется по протоколу 12С: соединяем контакты NodeMCU D3 (GPIO0) и D4 (GPIO2) соответственно с контактами SCL и SDA модуля DS3231 (рис. 17.10). Теперь нам необходимо получать из Интернета актуальный курс валют. Курсы валют в формате XML доступны на сайте Сбербанка по адресу: vnvw.cbr.ru/ scripts/XML_daily.asp?date_req=<data>, где <data> — дата в формате dd/mm/yyyy. Если параметр datereq в запросе отсутствует, то вы получите данные на последнюю зарегистрированную дату. Чтобы делать запросы по адресу получения XML-файла, сначала мы устанавливаем соединение с точкой доступа для подключения к Интернету: WiFi.mode (WIFI_STA) ; WiFi.begin(ssid, password); Затем создаем TCP-соединение с сервером cbr.ru: WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return;
406 Часть III. Практическое применение Arduino h - Н h - I L. — I u— l H H H H H и H — H H H H I - i : i - Рис. 17.10. Схема подключения модуля DS3231 к модулю NodeMCU ESP8266 Формируем строку для запроса XML-файла: String url = "/scripts/XML_daily.asp?date_req="+getDateStr(); Serial.print("Requesting URL: "); Serial.println(url); Процедура getDatestr () выдает строку в формате dd/mm/yyyy на текущий день: String getDatestr() { String str = String(day) + "/" + formatDigit(month, 2) + "/20" + formatDigit(year, 2);
Глава 17. Программирование в среде Arduino IDE других плат 407 return str; } Отправляем данные на сервер: client.print(String("GET ") + url + " HTTP/l.l\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); delayA0); И выводим в последовательный порт ответ сервера: String line; while(client.available()){ line = client.readStringUntil('\rf); Serial.print(line); delayA0); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i7\_i7JJ сопровождающего книгу электронного архива (см. приложение 2). Загружаем этот скетч в плату NodeMCU и видим результат его работы в мониторе последовательного порта (рис. 17.11). Работа со строками в Arduino достаточно проблемна, поэтому внесем в наш скетч следующие изменения: int k=0; while (client.available ()) { line = client.readStringUntil('\rf); if(k>0) k++; if(line.substringA1,14)=="840") k=l; if(k==5) { if(dollar==line.substring(9,16)) ; // если курс изменился - выводим на печать else { dollar=line.substring(9,16); printer.println(); printer.println(getTimeStr()); printer.print("$= "); printer.print(dollar); printer.print(" rub"); printer.printlnO ; delayA0);
408 Часть III. Практическое применение Arduino Рис. 17.11. Результат получения XML-файла с сайта cbr.ru в мониторе последовательного порта Рис. 17.12. Вывод курса доллара с сайта cbr.ru на принтер
Глава 17. Программирование в среде Arduino IDE других плат 409 Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i7\_17_03 сопровождающего книгу электронного архива (см. приложение 2). Загружаем этот скетч в плату NodeMCU и видим, как на печать выводится курс доллара (рис. 17.12). 17.2. Z-Uno — плата для прототипирования устройств Z-Wave Z-Wave — это распространенный протокол радиопередачи данных, предназначенный для домашней автоматизации. Передача данных осуществляется на частотах 869,0 МГц (Россия), 868,42 МГц (Европа, страны СЕРТ, Китай, Сингапур, ОАЭ, ЮАР), 908,42 МГц (США, Мексика), 921,42 МГц (Австралия, Бразилия, Новая Зеландия), 919,8 МГц (Гонконг), 865,2 МГц (Индия), 868,2 МГц (Малайзия), 951-956 и 922-926 МГц (Япония). Модуляция FSK (частотная). Скорость передачи: 42, 100 и 9,6 Кбит/с (для совместимости со старыми устройствами). Скважность не более 1%. Предельная мощность передачи — 1 мВт. Протокол Z-Wave весьма популярен, и существует огромное множество совместимых устройств. Однако все, кто когда-либо автоматизировал свое жилище, сталкивались с тем, что чего-то все-таки не хватает. Совсем недавно для облегчения разработки устройств Z-Wave было создано устройство Z-Uno (рис. 17.13). Более правильно назвать Z-Uno платформой, а не устройством, т. к. устройств на этой маленькой плате можно сделать много. Разработчик устройства — компания Z-Wave.me. Рис. 17.13. Плата Z-Uno
410 Часть III. Практическое применение Arduino Z-Uno — это Arduino в мире Z-Wave. На этой плате вы можете собрать все ваши устройства. Код пишется на языке С в стиле Arduino прямо в среде Arduino IDE, Эта же среда используется и для загрузки кода в плату по USB (есть также возможность залить новый код по радио через контроллеры Z-Wave — так называемая перепрошивка ОТА). Код управляет всеми выводами платы, как в Arduino. Привычный набор функций Arduino дополнен специфическими для работы с сетью Z-Wave— получения и отправки команд. Все сложности Z-Wave скрыты «под капотом» Z-Uno. Основное отличие Z-Uno от плат Arduino — наличие радиопередатчика Z-Wave и полная поддержка сетей этого стандарта. В России продаются более сотни совместимых с Z-Wave устройств — различных датчиков и контроллеров, а всего в мире реализуется свыше 1400 наименований от более чем четырехсот производителей. Сеть Z-Wave представляет собой самоорганизующуюся ячеистую mesh-сеть. То есть при отсутствии получателя сигнала в зоне прямой видимости сигнал к нему пойдет через соседние радиоузлы сети до тех пор, пока не будет найден получатель. Каждое устройство перед началом работы должно быть добавлено в сеть. Сеть Z-Wave может содержать до 232 устройств, каждое из которых имеет свой уникальный 8-битный идентификатор (ГО). Технические характеристики платы Z-Uno: ? 28 Кбайт флеш-памяти для скетчей; ? 4 Кбайт RAM; ? Z-Wave радиотрансивер на каналах 9,6, 40 и 100 Кбит/с; ? 22 вывода GPIO (некоторые перекрываются с другими функциями); ? 4 вывода АЦП; ? 5 выводов ШИМ; ? 2 вывода UART; П вход USB (в режиме serial); ? 64 Кбайт EEPROM; ? вывод SPI (режимы master или slave); ? 4 ИК-контроллера и один ИК-приемник с функцией обучения; ? по одному контроллеру TRIAC/ZEROX для диммирования; ? одно прерывание; ? 2 таймера A6 МГц или от внешнего источника); ? вывод 12С (программный на «ногах» GPIO); ? вывод 1-Wire (программный на «ногах» GPIO); ? 8x6 сканер кнопок (в том числе в режиме глубокого Сна); ? 2 сервисных светодиода, одна сервисная кнопка; ? один пользовательский светодиод (как контакт 13 у Arduino). Назначение контактов платы Z-Uno показано на рис. 17.14.
Глава 17. Программирование в среде Arduino IDE других плат 411 Рис. 17.14. Назначение контактов платы Z-Uno 17.2.1. Установка Arduino IDE для Z-Uno Пользовательские скетчи заливаются в Z-Uno из Arduino ГОЕ. Для работы в Arduino IDE с Z-Uno необходимо установить соответствующий пакет, который содержит компилятор, загрузчик, библиотеки и Н-файлы проекта Z-Uno. Это делается из Менеджера плат (Board Manager), появившегося в Arduino ГОЕ, начиная с версии 1.6.5. Итак, запускаем Arduino ГОЕ версии не ниже 1.6.5. Выполняем команду меню Файл | Настройки, в поле Additional Boards Manager URLs добавляем адрес: z-uno. z-wave. me/ files /z-uno/package_z -wave .me_index. j son и нажимаем кнопку OK (рис. 17.15). Перезапускаем Arduino IDE. Выполняем команду меню Инструменты | Плата | Boards Manager, находим в списке плат Z-Uno by Z-WANE>ME и нажимаем кнопку Install (рис. 17.16). После загрузки Плата Z-WANE>ME Z-Uno появится в списке плат (рис. 17.17). Пункт меню Записать Загрузчик (см. рис. 17.17) позволяет обновить загрузчик скетча Z-Uno и стек Z-Wave до самой последней версии, входящей в пакет Z-Uno. Также в меню появится флажок для включения шифрования (класс команд Security), компактной отправки пакетов (класс команд Multicommand) и рабочей частоты Frequency (RU, EU, USA). Эти функции не поддерживаются некоторыми
412 Часть III. Практическое применение Arduino Рис. 17.15. Добавление в поле Additional Boards Manager URLs адреса для скачивания пакета поддержки проекта Z-Uno Рис. 17.16. Процесс скачивания и установки пакета поддержки проекта Z-Uno
Глава 17. Программирование в среде Arduino IDE других плат 413 Рис. 17.17. Выбор платы Z-Uno в Arduino IDE Рис. 17.18. Примеры использования платы Z-Uno в Arduino IDE
414 Часть III. Практическое применение Arduino контроллерами. В меню Файл | Образцы появятся примеры, специфичные для Z-Uno (рис. 17.18). С них и можно начать изучение возможностей Z-Uno. 17.2.2. Подключение к плате Z-Uno датчика влажности DHT11 Рассмотрим проект подключения датчика DHT11 к плате Z-Uno и отправку значений влажности и температуры в каналы Multilevel Sensor. Схема соединений показана на рис. 17.19. Содержимое скетча приведено в листинге 17.2 (для программирования используется библиотека ZUNODHT). Рис. 17.19. Монтажная схема подключения к плате Z-Uno датчика DHT11 Листинг 1Т.2 // добавить библиотеку #include "ZUNO_DHT.h" // пин подключения data DHT11 #define DHTPIN 9 DHT dht(DHTPIN, DHT11); int humidity; // переменная для данных влажности int temperature; // переменная для данных температуры // установить каналы для отправки ZUNO_SETUP_CHANNELS( ZUNO_SENSOR_MULTILEVEL_TEMPEI^TURE (getterTemperature), ZUNO_SENSOR_MULTILEVEL_HUMIDITY(getterHumidity)
Глава 17. Программирование в среде Arduino IDE других плат 415 void setup () { dht. begin (); Serial.begin(); Serial.println("start"); void loop() { // получение данных с датчика humidity = dht.readHumidity(); temperature = dht.readTemperature(); Serial.print("Humidity = "); Serial.print(humidity); Serial.print(" % "); Serial.print("Temperature = "); Serial.print(temperature); Serial.println(" *C"); // отправка данных в каналы zunoSendReportA); zunoSendReportB); // пауза 30 секунд delayC0000); } byte getterTemperature() { return temperature; } byte getterHumidity() { return humidity; В окне канала Z-Wave данные температуры и влажности после добавления устройств будут выглядеть следующим образом (рис. 17.20). Рис. 17.20. Отображение данных в окне канала Z-Wave
ЧАСТЬ IV Интересные проекты на Arduino Глава 18. Умная теплица Глава 19. GPS-трекер и онлайн-сервис поиска стоянок Глава 20. Проекты для вендинга: купюроприемник, монетоприемник, разменный автомат Глава 21. Создание управляющей платы для автомойки самообслуживания Глава 22. Arduino и интерфейс USB: управление роботами Глава 23. Камера Pixy: реализация компьютерного зрения Глава 24. Проекты на плате Nano 33 BLE Sense
ГЛАВА 18 Умная теплица О0 Теплицы предназначены для обеспечения оптимального микроклимата, способствующего росту и развитию растений. Это могут быть и большие промышленные сооружения, и небольшое место на подоконнике для выращивания любимого цветка. Но даже за самой крохотной теплицей на подоконнике нужен уход: осуществлять полив, поддерживать нужную температуру, уровень освещенности и т. п. Многие с удовольствием занялись бы подобным хозяйством, вот только ни сил, ни времени на это нет. И только мечта подсказывает: вот бы иметь такую конструкцию, которая была бы настолько умной, что делала бы все сама. Такая теплица окажется востребованной теми, кто не хочет тратить много времени на уход за растениями, а также не имеет для этого возможности в случае длительного отсутствия — командировок, отпуска и т. п. Назовем такую теплицу «умной» и приступим к ее созданию. Какие же функции будет выполнять наша теплица? Прежде всего, нам потребуется оперативно получать всю необходимую информацию о климатических параметрах нашей теплицы: температуре и влажности воздуха, температуре и увлажненности почвы, а также освещенности внутри теплицы, т. е. осуществлять мониторинг климатических параметров теплицы. Какую проблему решит функция мониторинга? Прежде всего — устранит беспокойство насчет того, все ли в порядке с растениями во время нашего отсутствия: есть ли вода в системе, не выключалось ли электричество, может ли система вентиляции обеспечить нужную температуру, если в помещении стало слишком жарко, и т. п. Выводить данные мониторинга можно на дисплей при самой теплице, или оповещать нас о критических значениях климатических параметров с помощью «тревожных» светодиодов, или пересылать нам данные через Интернет на смартфон или планшет. Следующая функция — обеспечение автономности теплицы: при снижении уровня увлажненности почвы в теплице ниже определенного значения — включить полив, при снижении в ней температуры— включить обогрев, при превышении-
420 Часть IV. Интересные проекты на Arduino температуры — включить вентиляцию, освещенность в теплице тоже необходимо регулировать по определенному циклу. К функции автономности тесно примыкает и следующая функция — необходимо реализовать возможность управления теплицей: осуществлять полив, обогрев, вентиляцию, регулировать освещенность растений. Управление можно организовать с помощью автоматики или удаленно — через Интернет со смартфона или планшета (рис. 18.1). Попив, свет, вентилятор Network Данные с датчиков §*-- t, h, освещенность Данные с датчиков t, h, освещенность Полив, свет, вентилятор Рис. 18.1. Схема взаимодействия коипонентов умной теплицы 18.1. Мониторинг климатических параметров умной теплицы Начнем наш проект с реализации функции мониторинга параметров умной теплицы и обеспечим получение следующих данных об окружающей среде внутри нее: ? температура воздуха; ? влажность воздуха; О увлажненность почвы; ? освещенность цветка. Для реализации функции мониторинга нам понадобятся следующие компоненты: ? плата Arduino Uno; ? кабель USB; ? плата прототипирования;
Глава 18. Умная теплица 421 ? соединительные провода—15 шт.; ? фоторезистор; ? резистор ЮкОм; ? датчик температуры ТМРЗ6; ? датчик температуры и влажности воздуха DHT11; П модуль влажности почвы. Наборы компонентов «Для мейкеров» Многие из упомянутых в этом и других проектах компонентов имеются в наборах электроники «Для мейкеров», предлагаемых издательством «БХВ-Петербург» (см. https:// bhv.ru/product-category/nabory4llya-mejkerov/). Познакомимся с датчиками, которые обеспечат функции мониторинга параметров нашего проекта. Датчик температуры и влажности воздуха DHT11 мы рассматривали в главе 9 (см. разд. PJ). С помощью фоторезистора (рис. 18.2) осуществляется измерение освещенности: в темноте сопротивление фоторезистора весьма велико, но когда на него попадает свет, это сопротивление падает пропорционально его освещенности. Pin1 Pin 2 Pln3 DC voltage Analog eND ¦2J-5.5V Voltage Output Рис. 18.2. Фоторезистор Рис. 18.3. Аналоговый датчик температуры ТМР36 Аналоговый датчик температуры ТМР36 (рис. 18.3) позволяет легко преобразовать выходной уровень напряжения в показания температуры в градусах Цельсия. Каждые 10 мВ соответствуют 1°С. Формула для преобразования выходного напряжения в температуру: Т,°С = [(Vout, мВ - 500] / 10.
422 Часть IV. Интересные проекты на Arduino Модуль влажности почвы (рис. 18.4) предназначен для определения влажности земли, в которую погружен. Он позволяет узнать о недостаточном или избыточном поливе ваших домашних или садовых растений. Модуль состоит из двух частей: контактного щупа YL-28 и датчика YL-38, соединенных между собой двумя проводами. Между двумя электродами щупа YL-28 создается небольшое напряжение. Если почва сухая, то сопротивление велико и ток будет меньше. Если земля влажная — сопротивление меньше, ток — чуть больше. По итоговому аналоговому сигналу можно судить о степени влажности. Рис. 18.4. Модуль влажности почвы Монтажная схема этого проекта представлена на рис. 18.5, а его начальный общий вид — на рис. 18.6. Рис. 18.5. Монтажная схема подключения к плате Arduino датчиков для мониторинга параметров проекта умной теплицы
Глава 18. Умная теплица 423 Рис. 18.6. Проект умной теплицы: подключение датчиков Содержимое скетча, обеспечивающего работу этого проекта, приведено в листинге 18.1. Фоторезистор, датчик температуры ТМР36 и модуль влажности почвы — обычные аналоговые датчики. Аналоговые значения датчика ТМР36 мы преобразовываем в показания температуры в градусах Цельсия. Для работы с датчиком температуры и влажности воздуха DHT11 подключается Arduino-библиотека DHT. Данные с датчиков снимаются с интервалом 5 с и значения выводятся (пока!) в последовательный порт Arduino. Электронный архив Библиотека DHT размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Создадим в Arduino ШЕ новый скетч, занесем в него код из листинга 18.1 и загрузим этот скетч в плату Arduino. // подключение библиотеки DHT #include "DHT.h" // тип датчика DHT #define DHTTYPE DHT11 // контакт подключения входа данных модуля DHT11 int pinDHTll=9;
424 Часть IV. Интересные проекты на Arduino // контакт подключения аналогового выхода.модуля влажности почвы int pinSoilMoisture=AO; // контакт подключения аналогового выхода датчика температуры ТМРЗб int pinTMP36=Al; // контакт подключения аналогового выхода фоторезистора int pinPhotoresistor=A2; // создание экземпляра объекта DHT DHT dht(pinDHTll, DHTTYPE); void setup() { // запуск последовательного порта Serial.begin(9600); dht.beginO ; void loop () { // получение данных с DHT11 float h = dht.readHumidity(); if (isnan(h)) { Serial.println("Failed to read from DHT"); } else { Serial.print("HumidityDHTll= "); Serial.print(h)/Serial.println(" %") } // получение значения с аналогового вывода модуля влажности почвы int valO=analogRead(pinSoilMoisture); Serial.print("SoilMoisture= "); Serial.printIn(valO); // получение значения с аналогового вывода датчика температуры ТМР36 int vall=analogRead(pinTMP36); // перевод в мВ int mV=vall*1000/1024; // перевод в градусы Цельсия int t=(mV-500)/10; Serial.print("TempTMP36= "); Serial.print(h)/Serial.println(" C"); // получение значения с аналогового вывода фоторезистора int val2=analogRead(pinPhotoresistor); Serial.print("Light= "); Serial.println(val2); // пауза 5 с Serial.println( ); delayE000);
Глава 18. Умная теплица 425 Электронный архив Полный вариант рассмотренного скетча находится в папке examplesU8\_18_0i сопровождающего книгу электронного архива (см. приложение 2). Загрузив скетч в плату, открываем монитор последовательного порта и наблюдаем вывод в него показаний наших датчиков (рис. 18.7). HumiaityuHiii» ja.uu % SoilMoisHumidityDHTll» 38.00 % SoilMoisture» 531 ГетрТМРЗб- 23 С Light» 589 HumidityDHTll- 38.00 % SoilMoisture» 530 ГетрТМРЗб» 23 С Light- 586 HumidityDHTll- 38.00 % SoilMoisture- 530 ТегарТМРЗб» 23 С Light- 607 HumidityDKTll» 38.00 % SoilMoisture» 530 ГетрТМРЗб» 23 С Light» 598 HuiidityDHTll- 38.00 % SoilMoisture» 531 ГетрТМРЗб» 23 С Light- 668 HumidityDKTll» 38.00 % SoilMoisture» 530 ТетрТМРЗб» 23 С Light- 621 HumidityDKTll- 38.00% SoilMoisture» 530 ГетрТМРЗб- 23 С Light- 576 • Рис. 18.7. Вывод показаний наших датчиков в монитор последовательного порта Arduino 18.2. Индикация показаний умной теплицы Смотреть на показания датчиков через монитор последовательного порта не всегда приемлемо, поэтому мы создадим более удобную систему индикации показаний. Для этого мы, во-первых, реализуем вывод данных с датчиков на дисплей, а во- вторых, подключим светодиоды, которые будут сигнализировать о наступлении неблагоприятных климатических условий, требующих нашего вмешательства (например, о понижении увлажненности почвы, слишком высокой температуре, недостаточной освещенности).
426 Часть IV. Интересные проекты на Arduino Дополнительно к компонентам, использовавшимся в разд. 75.7, нам понадобятся также: ? светодиод красный — Зшт.; ? резистор 220 Ом — 3 шт.; ? ЖК-дисплей Nokia5110. Выбор двухцветного графического ЖК-дисплея Nokia5110 (рис. 18.8) обусловлен его дешевизной и умеренным энергопотреблением, позволяющим подключать этот дисплей к плате Arduino без дополнительного питания (см. также разд. 10.3). Рис. 18.8. ЖК-дисплей Nokia5110 Монтажная схема развития нашего проекта с учетом дополнительных компонентов (светодиодов и ЖК-дисплея) представлена на рис. 18.9. Приступим к написанию скетча и определим условия, при которых необходимо сигнализировать светодиодами о наступлении неблагоприятных климатических параметров: ? температура воздуха — выше tempdetect; ? увлажненность почвы — ниже moisturedetect; ? освещенность — ниже lightdetect. Значения для констант temp_detect, moisture_detect и light_detect необходимо определить самостоятельно. При наступлении неблагоприятного параметра будет
Глава 18. Умная теплица 427 Рис. 18.9. Монтажная схема подключения к плате Arduino датчиков, светодиодов и ЖК-дисплея для проекта умной теплицы загораться соответствующий светодиод, сигнализирующий нам, что необходимо предпринять какие-то действия: О включить полив почвы; О включить лампу освещения; ? включить вентилятор. Контакты (пины) Arduino для подключения светодиодов определены константами I?D_TEMP, LED_MOISTURE И LED__LIGHT. Текущие значения температуры и влажности воздуха, увлажненности почвы и освещенности будут выводиться на дисплей Nokia5110. Для работы с дисплеем подключаются Arduino-библиотеки Adafruit_GFX и Adafruit_PCD8544. Электронный архив Библиотеки Adafruit_GFX и Adafruit_PCD8544 размещены в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Создадим в Arduino ГОЕ новый скетч, занесем в него код из листинга 18.2 и загрузим этот скетч в плату Arduino. // подключение библиотек для nokia5110 #include <Adafruit_GFX.h> #include <Adafruit PCD8544.h>
428 Часть IV. Интересные проекты на Arduino /I подключение библиотеки DHT #include "DHT.h" // тип датчика DHT #define DHTTYPE DHT11 // контакт подключения входа данных модуля DHT11 int pinDHTll=9; // контакт подключения аналогового выхода модуля влажности почвы int pinSoilMoisture=AO; // контакт подключения аналогового выхода датчика температуры ТМР36 int pinTMP36-Al; // контакт подключения аналогового выхода фоторезистора int pinPhotoresistors=A2; // пины светодиодов индикации #define LED_TEMP 5 #define LED_MOISTURE 6 #define LED_LIGHT 7 // значения для условий #define TEMPJDETECT 30 #define MOISTUREJDETECT 500 #define LIGHT_DETECT 250 // создание экземпляра объекта DHT DHT dht(pinDHTll, DHTTYPE); // Nokia 5110 // pin 13 - Serial clock out (SCLK) // pin 12 - Serial data out (DIN) // pin 11 - Data/Command select (D/C) // pin 10 - LCD chip select (CS) // pin 8 - LCD reset (RST) Adafruit_PCD8544 display = Adafruit_PCD8544A3, 12, 11, 10, 8); void setup() { // запуск последовательного порта Serial.begin(9600); // pinMode (LEDJTEMP, OUTPUT) ,-digitalWrite (LED_TEMP, LOW) ; pinMode (IJSDJtolSTURE, OUTPUT) ;digitalWrite (LED_MOISTORE, LOW) ; pinMode (LED_LIGHT,OUTPUT) ;digitalWrite (LED_LIGHT, LOW) ; // dht.beginO; // инициализация дисплея display.begin(); // установить контраст фона экрана display.setContrastF0); display.clearDisplay(); // очистить экран display.setTextSize(l); // размер шрифта display.setTextColor(BLACK); // цвет
Глава 18. Умная теплица . 429 I/ заставка display.setCursorA5,15); display.print("Home Flower"); display.display(); delayB000); void loop() { display.clearDisplay(); display.setCursorE, 0); display.print("Home Flower"); // получение данных с DHT11 float h = dht.readHumidity(); display.setCursorE,10); if (isnan(h)) { Serial.println("Failed to read from DHT"); display.print("airH= error"); } else { Serial.print("HumidityDHTll= "); Serial.print(h);Serial.println(" %"); display.print("airH=");display.print(h);display.print("%"); } // получение значения с аналогового вывода модуля влажности почвы display.setCursorE,20); int valO=analogRead(pinSoilMoisture); Serial.print("SoilMoisture= "); Serial.println(valO); display.print("soilM=");display.print(valO); // получение значения с аналогового вывода датчика температуры ТМРЗб display.setCursorE,30); int vall=analogRead(pinTMP36); // перевод в мВ int mV= vall*1000/1024; // перевод в градусы Цельсия int t=(mV-500)/10+75;//t=23; Serial.print("ТетрТМРЗб- "); Serial.print(t);Serial.println(" C"); display.print("airT=");display.print(t);display.print(" C"); // получение значения с аналогового вывода фоторезистора display.setCursorE,40); int val2=analogRead(pinPhotoresistor); Serial.print("Light= "); Serial.println(val2); display.print("Light=");display.print(val2); // обновить display.display();
430 Часть IV. Интересные проекты на Arduino //// проверка условий // увлажненность почвы if(valO > MOISTURE_DETECT) digitalWrite(LED_MOISTURE,HIGH); else digitalWrite(LED_MOISTORE,LOW); // температура воздуха if(t > TEMP_DETECT) digitalWrite(LED_TEMP,HIGH); else digitalWrite (LEDJTEMP, LOW) ; // освещенность if(val2 < LIGHT_DETECT) digitalWrite(LED_LIGHT,HIGH); else digitalWrite(LED_LIGHT,LOW); // пауза 5 с Serial.println(); delayE000); HumidityDHTii- 38.00 % SoilMoisHumidityDHTll- 38.00 % SoilMoisture- 531 ТешрТМРЗб- 23 С Light- 589 HumidityDHTll- 38.00 % SoilMoisture- 530 ГеирТМРЗб- 23 С Light- 586 HumidityDHTll- 38.00 % SoilMoisture- 530 ГеирТМРЗб- 23 С Light- 607 HumidityDKTll» 38.00 % SoilMoisture- 530 ГешрТМРЗб- 23 С Light- 598 HumidityDKTll- 38.00 % SoilMoisture- 531 ТешрТМРЗб- 23 С Light- 668 HumidityDHTll- 38.00 % SoilMoisture- 530 ГешрТМРЗб- 23 С Light- 621 HumidityDHTll- 38.00 % SoilMoisture- 530 ТеярТМРЗб- 23 С Light- 576 Рис. 18.10. Вывод показаний датчиков в монитор последовательного порта Arduino
Глава 18. Умная теплица 431_ Электронный архив Полный вариант рассмотренного скетча находится в папке examples\18\_18__02 сопровождающего книгу электронного архива (см. приложение 2). После загрузки скетча в плату показания наших датчиков выводятся не только в монитор последовательного порта (рис. 18.10), но и на дисплей (рис. 18.11), а также о наступлении неблагоприятных климатических условий сигнализируют све- тодиоды. Рис. 18.11. Вывод показаний датчиков на дисплей Nokia5110 18.3. Организация полива, обдува и освещения в умной теплице Добавим нашей умной теплице функции управления — организуем полив цветка, его обдув и освещение. Выполняться эти операции будут по нажатию соответствующих кнопок. Дополнительно к компонентам, использовавшимся в предыдущих разделах, нам понадобятся также: ? плата расширения Relay shield на 4 реле; ? вентилятор 12 В; ? мембранный насос 12 В; ? лампа освещения;
432 Часть IV. Интересные проекты на Arduino ? кнопки — 3 шт.; ? резистор 10 кОм — 3 шт. Для полива почвы мы воспользуемся мембранным вакуумным насосом (рис. 18.12) — он способен всасывать воду из емкости и подавать ее в нужное место. Рабочее напряжение насоса — 12 В, потребляемый рабочий ток — 0,5-0,7 А, расход воды — 1,5 л/мин. К насосу необходимо прикрепить шланги требуемой длины. Рис. 18.12. Мембранный вакуумный насос При превышении внутри теплицы установленных значений температуры воздуха мы будем производить обдув цветка с помощью вентилятора. Вентилятор можно взять любой, работающий от 12 В,— например, из старого системного блока (рис. 18.13). Рис. 18.13. Вентилятор 12 В Искусственные источники света для эффективного выращивания растений должны излучать спектр, аналогичный тому, который получают растения в естественной среде. Если полной аналогии достичь сложно, то освещение должно удовлетворять
Глава 18. Умная теплица 433 хотя бы минимальным потребностям. Чтобы обеспечить наиболее комфортные для развития растений условия, подбираются специальные лампы, оказывающие на них различное влияние. Рекомендуется использовать следующие лампы: ? светодиодные фитолампы; ? энергосберегающие лампы дневного спектра; ? люминесцентные. Насос, вентилятор и лампу подключать напрямую к Arduino нельзя! И нам придется обеспечить управление ими через реле. Можно воспользоваться, например, платой расширения Relay shield (рис. 18.14), которая содержит 4 реле с необходимой обвязкой. Рис. 18.14. Плата расширения Relay shield Монтажная схема развития нашего проекта с учетом дополнительных компонентов представлена на рис. 18.15. Допишем код скетча с учетом сделанных в схеме дополнений. Для этого создадим переменные типа Boolean (true— включено, false— выключено) для состояния трех реле: ? насос — statusjpump; ? лампа — status__lamp; ? вентилятор — statusfun. В цикле loop о отслеживаем нажатие кнопок с проверкой на дребезг (процедура debounce ()) и при нажатии кнопки изменяем статус соответствующей переменной, отправляя команду для изменения статуса соответствующего реле на противоположное:
434 Часть IV. Интересные проекты на Arduino I ^ "<l? Hi « ft l: "• 220 Б Рис. 18.15. Монтажная схема развития проекта умной теплицы: мониторинг параметров и ручное управление ? включение/выключение насоса (полив почвы); ? включение/выключение освещения; О включение/выключение вентилятора. Создадим в Arduino IDE новый скетч, занесем в него код из листинга 18.3 и загрузим этот скетч в плату Arduino. // подключение библиотек для nokia5110 #include <Adafruit_GFX.h> #include <Adafruit PCD8544.h>
Глава 18. Умная теплица 435 II подключение библиотеки DHT #include "DHT.h" // тип датчика DHT #define DHTTYPE DHT11 // контакт подключения входа данных модуля DHT11 int pinDHTll=9; // контакт подключения аналогового выхода модуля влажности почвы int pinSoilMoisture=AO; // контакт подключения аналогового выхода датчика температуры ТМРЗб int pinTMP3 6=А1; // контакт подключения аналогового выхода фоторезистора int pinPhotoresistor=A2; // пины светодиодов индикации #define LEDJTEMP 5 #define LED_MOISTURE 6 #define LED_LIGHT 7 // значения для условий #define TEMP_DETECT 30 #define MOISTURE_DETECT 500 #define LIGHT_DETECT 250 // кнопки #define COUNT_BUTTONS 3 int pinButtons[]={A4,A5,A6}; int lastButtons[] = {0,0,0,0}; int currentButtons[] = {0,0,0,0}; // реле int pinRelays[]={2,3,4}; // статусы полива, освещения, вентилятора boolean statusRelays[]={false,false,false}; // создание экземпляра объекта DHT DHT dht(pinDHTll, DHTTYPE); // Nokia 5110 // pin 13 - Serial clock out* (SCLK) // pin 12 - Serial data out (DIN) // pin 11 - Data/Command select (D/C) // pin 10 - LCD chip select (CS) // pin 8 - LCD reset (RST) Adafruit_PCD8544 display = Adafruit_PCD8544A3, 12, 11, 10, 8); unsigned long millisupdate=0;
436 Часть IV. Интересные проекты на Arduino void setup() { // запуск последовательного порта Serial.begin(9600); // pinMode (LED_TEMP, OUTPUT);digitalWrite (LEDJTEMP, LOW) ; pinMode (LED_MOISTURE,OUTPUT) /digitalWrite (LED_MOISTORE, LOW) ; pinMode (LED_LIGHT, OUTPUT) ;digitalWrite (LED_LIGHT,LOW) ; // dht,begin(); // инициализация дисплея display.begin(); // установить контраст фона экрана display.setContrastF0); display.clearDisplay(); // очистить экран display.setTextSize(l); // размер шрифта display.setTextColor(BLACK); // цвет // заставка display.setCursorA5,15); display.print("Home Flower"); display.display(); delayB000); void loopO { // каждые 5 с - получение показаний датчиков //и вывод на дисплей if (millis()-millisupdate>5000) { millisupdate=millis(); display.clearDisplay(); display.setCursorE,0) ; display.print("Home Flower"); // получение данных с DHT11 float h = dht.readHumidity(); display.setCursorE,10); if (isnan(h)) { Serial.println("Failed to read from DHT"); display.print("airH= error"); } else { Serial.print("HumidityDHTll= "); Serial.print(h);Serial.println(" %"); display.print("airH=");display.print(h);display.print("%");
Глава 18. Умная теплица 437 // получение значения с аналогового вывода модуля влажности почвы display.setCursorE,20); int valO=analogRead(pinSoilMoisture); Serial.print("SoilMoisture= "); Serial.println(valO); display.print("soilM=");display.print(valO); // получение значения с аналогового вывода датчика температуры ТМРЗб display.setCursorE,30); int vall=analogRead(pinTMP36); // перевод в мВ int mV= vall*1000/1024; // перевод в градусы Цельсия int t=(mV-500)/10+75;//t=23; Serial.print("ТетрТМРЗ6= "); Serial.print(t);Serial.println(" C"); display.print ("airT=");display.print (t) /display.print (" C") ; // получение значения с аналогового вывода фоторезистора display.setCursorE,40); int val2=analogRead(pinPhotoresistor); Serial.print("Light= "); Serial.println(val2); display.print("Light=");display.print(val2); // обновить display.display(); // вывод состояния полива, лампы, вентилятора Serial.print("pump - "); Serial.println(statusRelays[2]); Serial.print("fun - "); Serial.println(statusRelays[1]); Serial.print("lamp - "); Serial.println(statusRelays[0]); //// проверка условий // увлажненность почвы if(valO > MOISTURE_DETECT) digitalWrite(LED_MOISTORE,HIGH); else digitalWrite(LED_MOISTORE,LOW); // температура воздуха if (t > TEMP_DETECT) digitalWrite(LED_TEMP,HIGH); else digitalWrite (LEDJTEMP, LOW) ; // освещенность if(val2 < LIGHT_DETECT) digitalWrite (LED__LIGHT,HIGH) ; else digitalWrite (LED_LIGHT, LOW) ; // пауза 5 с Serial.println(); } // проверка нажатия кнопок выбора программ for(int i=0;i<COUNT BUTTONS;i
438 Часть IV. Интересные проекты на Arduino { currentButtons[i] = debounce(lastButtons[i],pinButtons[i]); if (lastButtons[i] == 0 && currentButtons[i] ==-1) // если нажатие... { doButtons(i); } lastButtons[i] = currentButtons[i]; // при нажатии кнопок void doButtons(int but) { // изменить статус statusRelays[but]=!statusRelays[but]; // изменить состояние реле digitalWrite(pinRelays[but],statusRelays[but]); /* Функция сглаживания дребезга * Принимает в качестве аргумента предыдущее состояние кнопки * и вьщает фактическое. */ int debounce(int last,int pinl) { int currents digitalRead(pinl); // Считать состояние кнопки if (last != current) // если изменилось... { delayE); // ждем 5 мс current = digitalRead(pinl); // считываем состояние кнопки return current; // возвращаем состояние кнопки Электронный архив Полный вариант рассмотренного скетча находится в папке examples\i8\__W_03 сопровождающего книгу электронного архива (см. приложение 2). После загрузки скетча в плату мы можем управлять включением/выключением насоса, лампы и вентилятора с помощью кнопок. В монитор последовательного порта выводятся показания датчиков и установленные нами состояния реле: полив, вентиляция, освещение (рис. 18.16), на дисплей— показания датчиков. Общий вид проекта с учетом добавленных компонентов приведен на рис. 18.17.
ГеирТМРЗб= 25 С Light= 362 pump - О fun - О lamp - О HumidityDHTll= 35.00 % SoilMoisture= 474 ГеярТМРЗб= 25 С Light= 362 pump - О fun - О lamp - О HufflidityDHTll= 35.00 % SoilMoisture= 459 ГешрТМР36= 25 С Light= 362 pump - 0 fun - 1 lamp - 1 • HuinidityDHTll= 35.00 % SoilMoistjure= 447 TempTMP36= 25 С Light= 361 pump - 0 fun - 0 lamp - 1 HumidityDHTll= 35.00 % SoilMoisture= 446 TempTMP36= 25 С Light= 366 pump - 0 fun - 1 lamp - 1 i 00 I Q> Рис. 18.16. Вывод показаний датчиков и состояний реле в монитор последовательного порта Arduino Рис. 18.17. Проект «Домашний цветок»: подключение реле, насоса и лампы со
440 Часть IV. Интересные проекты на Arduino 18.4. Переносим функции мониторинга и управления теплицей на устройство с ОС Android Перенесем функции мониторинга и управления теплицей на смартфон (или планшет) с операционной системой Android. Во-первых, это очень удобно, во-вторых, мы сможем исключить из системы ряд деталей, а именно — дисплей и кнопки. Эти функции возьмет на себя смартфон/планшет. Связь Arduino со смартфоном будет осуществляться по Bluetooth, и нам понадобится Bluetooth-модуль НС-05, позволяющий наладить двунаправленную радиосвязь по протоколу Bluetooth (этот модуль мы уже рассматривали ъразд. 12.5). Подключение Bluetooth-модуля к плате Arduino осуществляется по последовательному порту. Аппаратный последовательный порт Arduino у нас занят — через него осуществляется отладка программы, поэтому для связи с Bluetooth-модулем мы воспользуемся программным последовательным портом. В качестве контактов программного последовательного интерфейса мы задействуем цифровые выводы 17 и 18, которые освободятся после удаления из системы кнопок. Монтажная схема развития нашего проекта с учетом сделанных изменений представлена на рис. 18.18. Заметьте, что питание Bluetooth-модуля НС-05 — 3,3 В! Теперь нам надо удалить из предыдущего скетча фрагменты кода, связанные с выводом данных на дисплей и обработкой нажатий клавиш, и добавить отправку данных в Bluetooth-модуль НС-05 по SoftwareSerial, а также получение и обработку данных, поступающих по SoftwareSerial, для команд включения/выключения насоса, вентилятора и лампы. Формат команд отправки данных мониторинга в SoftwareSerial представлен в табл. 18.1, а формат команд управления из SoftwareSerial — в табл. 18.2. Таблица 18.1. Команды отправки данных мониторинга в SoftwareSerial Команда aH=<data>\r\n SM=<data>\r\n aT=<data>\r\n Ph=<data>\r\n PM=<data>\r\n FN=<data>\r\n LM=<data>\r\n Описание Данные влажности с датчика DHT11 Данные увлажненности ПОЧВЫ SoilMoisture Данные температуры с датчика ТМР36 Данные освещенности — с фоторезистора Состояние реле включения/выключения A/0) насоса Состояние реле включения/выключения A/0) вентилятора Состояние реле включения/выключения A/0) лампы
Глава 18. Умная теплица 441 Таблица 18.2. Команды управления из SoftwareSerial Команда РМ=1# РМ=0# FN=1# FN=0# LM=1# LM=0# Описание Включение насоса Выключение насоса Включение вентилятора Выключение вентилятора Включение лампы Выключение лампы " 2.20 S Рис. 18.18. Монтажная схема развития проекта умной теплицы: мониторинг параметров, обеспечение комфортных условий и связь по Bluetooth
442 Часть IV. Интересные проекты на Arduino Создадим в Arduino ШЕ новый скетч, занесем в него код из листинга 18.4 и загрузим этот скетч в плату Arduino. // подключение библиотеки SoftwareSerial #include <SoftwareSerial.h> // подключение библиотеки DHT #include "DHT.h" // тип датчика DHT #define DHTTYPE DHT11 // контакты подключения bluetooth-модуля НС-05 int pinBlRx=17; int pinBlTx=18; // контакт подключения входа данных модуля DHT11 int pinDHTll=9; // контакт подключения аналогового выхода модуля влажности почвы int pinSoilMoisture=AO; // контакт подключения аналогового выхода датчика температуры ТМР36 int pinTMP36=Al; // контакт подключения аналогового выхода фоторезистора int pinPhotoresistor=A2; // пины светодиодов индикации #define LED_TEMP 5 #define LED_MOISTURE 6 #define LED_LIGHT 7 // значения для условий #define TEMP_DETECT 30 #define MOISTURE_DETECT 500 #define LIGHT_DETECT 250 // реле int pinRelays[]={2,3,4}; // статусы полива, освещения, вентилятора boolean statusRelays[]={false,false,false}; // создание экземпляра объекта SoftwareSerial SoftwareSerial HC05Serial(pinBlRx,pinBlTx); // создание экземпляра объекта DHT DHT dht(pinDHTll, DHTTYPE); unsigned long millisupdate=O; // для получения данных из SoftwareSerial String inputStringO = "";
Глава 18. Умная теплица 443 II признак конца полученной строки boolean stringCompleteO = false; void setup () { // запуск последовательного порта Serial.begin(9600); // pinMode (LEDJTEMP, OUTPUT) ; digitalWrite (LED_TEMP, LOW) ; pinMode(LED_MOISTURE,OUTPUT)/digitalWrite(LED_MOISTURE,LOW); pinMode(LED_LIGHT,OUTPUT)/digitalWrite(LED_LIGHT,LOW); // инициализация dht dht.begin()/ // запуск SoftwareSerial HC05Serial.begin(9600)/ // резервирование 50 bytes для the inputString: inputStringO.reserveE0)/ void loop() { // ожидание конца строки для анализа поступившего запроса: serialEventO()/ if (stringCompleteO) { Serial.println(inputStringO)/ parse_stringO(inputStringO); // очистить : inputStringO = ""/ stringCompleteO = false/ } // каждые 5 с - получение показаний датчиков //и вывод на дисплей if (millis()-millisupdate>5000) { millisupdate^millis () / // получение данных с DHT11 float h = dht.readHumidityO/ if (isnan(h)) { Serial.println("Failed to read from DHT")/ HC05Serial.println("Hl=101")/ • delayA0)/ } else { ^ Serial.print("HumidityDHTll= ")/ Serial.print(h)/Serial.println(" %")/
444 Часть IV. Интересные проекты на Arduino HC05Serial.print("аН=");HC05Serial.print(h);HC05Serial.println(); delayA0); // получение значения с аналогового вывода модуля влажности почвы int valO=analogRead(pinSoilMoisture); Serial.print("SoilMoisture= "); Serial.println(valO); HC05Serial.print("SM=");HC05Serial.print(h);HC05Serial.println(); delayA0); // получение значения с аналогового вывода датчика температуры ТМРЗб int vall=analogRead(pinTMP36); // перевод в мВ int mV= vall*1000/1024; // перевод в градусы Цельсия int t=(mV-500)/10+75;//t=23; Serial.print("TempTMP36= "); Serial.print(t);Serial.printIn(" C"); HC05Serial.print("aT=");HC05Serial.print(t);HC05Serial.println(); delayA0); // получение значения с аналогового вывода фоторезистора int val2=analogRead(pinPhotoresistor); Serial.print("Light= "); Serial.println(val2); HC05Serial.print("Ph=");HC05Serial.print(val2);HC05Serial.printIn(); delayA0); // обновить // вывод состояние полива, лампы, вентилятора Serial.print("pump - "); Serial.println(statusRelays[2]); Serial.print("fun - "); Serial.println(statusRelays[1]); Serial.print("lamp - "); Serial.println(statusRelays[0]); HC05Serial.print("PM=");HC05Serial.print(statusRelays[2]); HC05Serial.print(" "); delayA0); HC05Serial.print("FN=");HC05Serial.print(statusRelays[1]); HC05Serial.print(" "); delay(lO); HC05Serial.print("LM=");HC05Serial.print(statusRelays[0]); HC05Serial. printing1 "); delayA0); //// проверка условий // увлажненность почвы if(valO > MOISTURE_DETECT) digitalWrite(LED_MOISTURE,HIGH); else digitalWrite(LED_MOISTURE,LOW); // температура воздуха if(t > TEMP_DETECT) digitalWrite(LED_TEMP,HIGH); else digitalWrite (LEDJTEMP, LOW);
Глава 18. Умная теплица 445 // освещенность if(val2 < LIGHT_DETECT) digitalWrite(LED_LIGHTfHIGH); else digitalWrite(LED_LIGHT,LOW); // пауза 5 с Serial.println(); // SerialEvent для НС05 void serialEventO() { while (HC05Serial.available()) { // получить очередной байт: char inChar = (char)HC05Serial.read() // добавить в строку inputStringO += inChar; // /n - конец передачи if (inChar — •#') { stringCompleteO = true; // парсинг строки из android void parse_stringO(String inputString) { // длина строки int lengthl=inputString.length(); if(lengthl!=5) {Serial.printIn("ERROR1"); return;} if(inputString.charAtB)!='=') {Serial.printIn("ERROR2"); return;} if(inputString.charAtD)!='#') {Serial.println("ERROR3"); return;} String paraml=inputString.substring@,2); int param2=inputString.substringC,4).tolnt(); Serial.print("paraml=");Serial.println(paraml); Serial.print("param2=");Serial.println(param2); if (paraml="PM") doCoinmand B, min (param2,1)); else if (paraml=="FNlf) doCommand A, min (param2, 1)); else if(paraml=="LM") doCommand @, min (param2, 1)); // исполнение команды от смартфона void doCommand(int relay, int statusl)
446 Часть IV. Интересные проекты на Arduino aH-34 5М-449 aT-24 Ph-222 РН-1 FH-0 LU-0 аН-ЗО SU-443 aT-24 Ph-239 РИ-1 FH-0 LM-0 вН-33 SM-446 aT-24 Ph-370 PU-1 FH-0 LU-0 аН-32 5М-446 aT-24 Ph-287 PU-1 FH-0 LU-0 вН-32 SU-440 aT-24 Ph-268 РИ-1 FH-0 LU-0 aH-34 SU-449 aT-24 Ph-404 PU-1 FH-0 LU-0 aH-31 SU-447 aT-24 Ph-392 PU-1 FH-Q LU-0 aH-33 SU-446 aT-24 Ph-310 PU-1 FH-0 LU-0 aH-30 SU-448 aT-24 Ph-366 PU-1 FH-0 LU-0 aH-34 SU-447 aT-24 Ph-261 PU-1 FH-0 LU-0 aH-33 SU-448 aT-24 Ph-309 PU-1 FH-0 LU-0 aH-33 SU-44Q aT-24 Ph-414 PU-1 FH-0 LU-0 aH-31 SU-440 aT-24 Ph-353 PU-1 FH-0 LU-0 32 SU-446 aT-24 Ph-394 PU-1 FH-0 LU-0 Рис. 18.19. Получение в приложение Bluetooth Terminal, установленное на планшете, данных мониторинга с платы Arduino aH-35 SM-44Q aT-24 Ph-334 PM-1 FH-0 LM-0 aH=35 SM-440 aT-24 Ph-333 PM-1 FN-0 LM-i aH«35 SM-440 aT-24 Ph-334 PM-1 FH-0 LM-0 aH-35 SM-440 aT-24 Ph-334 PM-1 FH-0 LM-0 FH-1# aH-35 SM-440 aT-24 Ph-334 PM-1 FH-1 LM-0 aH-35 SM-440 aT-24 Ph-333 PM-1 FH-1 LM-0 PM*Q# aH-35 SM-440 aT-24 Ph-334 PM-0 FH-1 LM-Q aH-35 SM-440 aT-24 Ph-333 PM-0 FH-1 LM-0 aH-35 SM-440 aT-24 Ph-334 PM-0 FH-1 LM-1 LM=O# aH-35 SM-440 aT-24 Ph-334 PM-0 FH-1 LM-0 Рис. 18.20. Отправка из приложения Bluetooth Terminal, установленного на планшете, команд управления на плату Arduino
Глава 18. Умная теплица 447 I/ изменить статус statusRelays[relay]=statusl; // изменить состояние реле digitalWrite(pinRelays[relay],statusRelays[relay]); Получать на смартфон/планшет данные и отправлять с него команды мы будем (пока!) из приложения Bluetooth Terminal, которое скачаем из Google Play. Установим его на смартфон/планшет, подключимся из Bluetooth Terminal к нашему Bluetooth-модулю и увидим на смартфоне/планшете отправленные с платы Arduino данные (рис. 18.19). Из этого же приложения мы отправляем на плату Arduino команды управления: включения/выключения насоса, вентилятора и лампы (рис. 18.20). 18.5. Создаем собственное мобильное приложение для управления умной теплицей Использовать приложение Bluetooth Terminal не очень удобно — хорошо бы создать для этого собственное полноценное приложение. Глубоко вникать в вопросы программирования для операционной системы Android не входит в наши планы, поэтому нам нужна простая и понятная система создания кода для Android — наподобие системы Sctratch для Arduino. К счастью, подобный визуальный редактор есть— это Арр Invertor 2, онлайн-редактор визуального программирования для Android, доступ к которому можно получить на странице: http://ai2.appinventoi\ mit.edu. После авторизации (можно использовать профиль Google) или регистрации попадаем в свой профиль программы, где можем создать новый проект (рис. 18.21). Рис. 18.21. Создание проекта в своем профиле Арр Invertor 2
448 Часть IV. Интересные проекты на Arduino Сначала в панели Designer создаем интерфейс нашего приложения (рис. 18.22), перетаскивая на экран необходимые компоненты. Кроме визуальных компонентов необходимо добавить три невизуальных: ? Bluetooth client — из раздела Connectivity; ? Clock— из раздела Sensors (для получения данных из Bluetooth с периодичностью, установленной в Clock); ? Notifer — из раздела Userlnterface. Рис. 18.22. Создание интерфейса приложения в панели Designer Затем переходим в раздел Block, где создаем код: ? для инициализации Bluetooth-соединения и создания Bluetooth-клиента; ? для отправки сообщений при изменении состояния флажков для насоса, вентилятора и лампы; ? для получения по таймеру сообщений, поступающих по Bluetooth из Arduino. После чего создаем Арр-приложение (рис. 18.23) и загружаем его на смартфон/планшет. Теперь нам надо внести небольшие изменения в наш скетч (см. листинг 18.4), заменив разделитель с пробела на символ * при отправке данных с Arduino на Android. Электронный архив Код этого скетча находится в папке examples\18\_18jO5 сопровождающего книгу электронного архива (см. приложение 2).
Глава 18. Умная теплица 449 Рис. 18.23. Генерация Арр-приложения Загружаем этот скетч в плату Arduino и запускаем на смартфоне/планшете наше приложение (рис. 18.24). Затем соединяемся из него по Bluetooth с платой Arduino (рис. 18.25) и получаем возможность наблюдать за состоянием датчиков нашей теплицы и отправлять команды управления поливом, обдувом и освещением (рис. 18.26). У {j/1 «Ж В € Н N ОС ? Ь Массе дл« ттлшэ ; Вентилятор Рис. 18.24. Приложение запущено на планшете Электронный архив Разработанное приложение и файл проекта находятся в папке exampfes\18\Android сопровождающего книгу электронного архива (см. приложение 2).
450 Часть IV. Интересные проекты на Arduino Рис. 18.25. Подключение приложения по Bluetooth к плате Arduino Рис. 18.26. Приложение в работе: получение данных с датчиков и отправка команд управления
Глава 18. Умная теплица 451 18.6. Превращаем нашу умную теплицу в объект Интернета вещей Теперь мы превратим нашу умную теплицу в объект Интернета вещей (IoT) — это позволит мониторить данные теплицы из любой точки мира, где у нас будет доступ к Интернету. Для этого необходимо организовать отправку этих данных на публичный интернет-сервер— например, на сайт «Народный мониторинг» (http:// narodmon.ru). Сайт «Народный мониторинг» — геоинформационный сервис, позволяющий отображать на карте мира и контролировать (на ПК, смартфонах и других гаджетах) показания датчиков своих участников (температуры, влажности, атмосферного давления, скорости и направления ветра, радиации, энергопотребления и многих других), а также трансляций частных и городских веб-камер для публичного или частного (приватного) просмотра (см. также главу 13). Дополнительно к компонентам, использовавшимся в предыдущих разделах этой главы, нам понадобится только один модуль — Ethernet shield W5100, который мы уже рассматривали в разд. 13.1. В скетч из предыдущего раздела мы внесем лишь незначительные изменения. При включении Arduino добавим запуск Ethernet и получение IP-адреса Ethernet- шилдом W5100. Процедура еthemet_begin () получает IP-адрес либо с помощью DHCP, либо назначает плате статический адрес (в зависимости от значения константы DHCP) (листинг 18.5). byte arduinojnac[] = { 0x35, 0x75, 0x02, OxFF, OxFF, 0x01 }; String MAC=5-75-02-FF-FF-01"; EthernetClient client; ttdefine DHCP 1 // 0-dhcp, 1 - manual // если адрес статический // (установить #define DHCP 1) int mip[4]={192,168,0,121}; int mmask[4]={255,255,255,0}; int mgateway[4]={192,168,0,28}; int mdns[4]={192,168,l,l}; // запустить ethernet void ethernet_begin() { if(DHCP==0) { if(Ethernet.begin(arduino_mac)==0) Serial.printIn("dhcp - error"); else Serial.println("dhcp - ok");
452 Часть IV. Интересные проекты на Arduino else { IPAddress arduino_ip(mip[0] ,mip[l] ,mip[2] fmip[3]); IPAddress dns_ip(mdns [0] ,mdns [1] ,mdns [2] ,mdns [3]); IPAddress gateway_ip(mgateway[0],mgateway[1],mgateway[2],mgateway[3]); IPAddress subnet_mask(ramask[O],mmask[l],mmask[2],ramask[3]); Ethernet.begin(arduino_mac, arduino_ip, dns_ip, gateway_ip, subnetjnask) ; Для загрузки данных на сервер «Народного мониторинга» необходимо отправить их методом GET на адрес: http://narodmon.ru/get?ID=MAC&macl=valuel&...&macN При каждом опросе датчиков формируем строку, но отправляем данные один раз в 5 мин. Итак, создадим в Arduino IDE новый скетч, занесем в него код из листинга 18.6 и загрузим этот скетч в плату Arduino. // подключение библиотеки SoftwareSerial #include <SoftwareSerial.h> // подключение библиотеки DHT , tinclude "DHT.h" // тип датчика DHT #define DHTTYPE DHT11 // контакты подключения bluetooth-модуля HC-05 int pinBlRx=17; int pinBlTx=18; // контакт подключения входа данных модуля DHT11 int pinDHTll=9; // контакт подключения аналогового выхода модуля влажности почвы int pinSoilMoisture=AO; // контакт подключения аналогового выхода датчика температуры ТМРЗб int pinTMP3 6=A1 ; // контакт подключения аналогового выхода фоторезйстора int pinPhotoresistor=A2; // пины светодиодов индикации #define LEDJTEMP 5 #define LED_MOISTURE 6 tdefine LED_LIGHT 7 // значения для условий #define TEMP DETECT 30
Глава 18. Умная теплица 453 #define MOISTUREJDETECT 500 tdefine LIGHT_DETECT 250 // реле int pinRelays[]={2,3,4}; // статусы полива, освещения, вентилятора boolean statusRelays[]={false,false,false}; // создание экземпляра объекта SoftwareSerial SoftwareSerial HC05Serial(pinBlRx,pinBlTx); // создание экземпляра объекта DHT DHT dht(pinDHTll, DHTTYPE); // для получения данных из SoftwareSerial String inputStringO = ""; // признак конца полученной строки boolean stringCompleteO = false; // Ethernet #include <SPI.h> tinclude <Ethernet.h> byte arduinojnac[] = { 0x35, 0x75, 0x02, OxFF, OxFF, 0x01 }; String MAC=5-75-02-FF-FF-01"; EthernetClient client; #define DHCP 1 // 0-dhcp, 1 - manual // если адрес статический // (установить #define DHCP 1) int mip[4]={192,168,0,121}; int mmask[4] = {255,255,255,0}; int mgateway[4]={192,168,0,28}; int mdns[4]={192,168,l,l}; // IoT-сервер char server[] = "narodmon.ru"; String strNarodmon=""; unsigned long millisupdatel=O; unsigned long millisupdate2=0; void setup() { // запуск последовательного порта Serial.begin(9600); // pinMode (LED_TEMP, OUTPUT) ;digitalWrite (LED_TEMP, LOW) ; pinMode (LED_MOISTURE,OUTPUT) ;digitalWrite (LEDJ1OISTORE, LOW) ; pinMode (LED_LIGHT, OUTPUT) ;digitalWrite (LED_LIGHT, LOW);
454 Часть IV. Интересные проекты на Arduino // инициализация dht dht.begin(); // запуск SoftwareSerial HC05Serial.begin(9600); // резервирование 50 bytes для the inputString: inputStringO.reserveE0); // установка сетевого соединения ethernet_begin(); print_ip () ; void loop() { // ожидание конца строки для анализа поступившего запроса: serialEventO(); if (stringCompleteO) { Serial.println(inputStringO); parse_stringO(inputStringO); // очистить : inputStringO = ""; stringCompleteO = false; } // каждые 5 с - получение показаний датчиков //и вывод в software serial на экран телефона if(millis()-millisupdatel>5000) { millisupdatel^millis(); s t rNa rodmon=" "; // получение данных с DHT11 float h = -dht.readHumidity(); if (isnan(h)) { Serial.println("Failed to read from DHT"); HC05Serial.println("Hl=101"); delayA0); } else { Serial.print("HumidityDHTll= "); Serial.print(h);Serial.println(" %"); HC05Serial.print("aH=");HC05Serial.print(h);HC05Serial.print("*"); strNarodmon=strNarodmon+"&Hl=f4St'ring (h) ; delayA0); } // получение значения с аналогового вывода модуля влажности почвы int valO=analogRead(pinSoilMoisture); Serial.print("SoilMoisture= "); Serial.println(valO);
Глава 18. Умная теплица 455 HC05Serial.print("SM=");HC05Serial.print(valO);HC05Serial.print("*"); strNarodmon=strNarodmon+"&H2="+String(map(valO, 0,1024,0,100)); delayA0); // получение значения с аналогового вывода датчика температуры ТМР36 int vall=analogRead(pinTMP36); // перевод в мВ int mV= vall*1000/1024; // перевод в градусы Цельсия int t=(mV-500)/10+75; Serial.print("ТетрТМРЗ6= "); Serial.print(t)/Serial.println(" C"); HC05Serial.print("aT=");HC05Serial.print(t);HC05Serial.print("*"); strNarodmon=strNarodmon+"&Tl=="+String (t); delayA0); // получение значения с аналогового вывода фоторезистора int val2=analogRead(pinPhotoresistor); Serial.print("Light= "); Serial.println(val2); HC05Serial.print("Ph=");HC05Serial.print(val2);HC05Serial.print("*"); strNarodmon=strNarodmon+"&Ll="+String(val2); delayA0); // обновить // вывод состояния полива, лампы, вентилятора Serial.print("pump - "); Serial.println(statusRelays[2]); Serial.print("fun - "); Serial.println(statusRelays[1]); Serial.print("lamp - "); Serial.println(statusRelays[0]); HC05Serial.print("PM=");HC05Serial.print(statusRelays[2]); HC05Serial.print("*"); delayA0); HC05Serial.print("FN=");HC05Serial.print(statusRelays[1]); HC05Serial.print("*"); delayA0); HC05Serial.print("LM=");HC05Serial.print(statusRelays[0]); delayA0); //// проверка условий // увлажненность почвы if(valO > MOISTURE_DETECT) digitalWrite(LED_MOISTURE,HIGH); else digitalWrite(LED_MOISTORE,LOW); // температура воздуха if(t > TEMP_DETECT) digitalWrite(LED_TEMP,HIGH); else digitalWrite(LED_TEMP,LOW); // освещенность if(val2 < LIGHT_DETECT) digitalWrite(LED_LIGHT,HIGH);
456 Часть IV. Интересные проекты на Arduino else digitalWrite (LED_LIGHT, LOW) ; Serial.println(); } if (millis()-millisupdate2>5*60000) { millisupdate2=millis(); if (client.connect(server, 80)) { client.print("GET /get?ID="); client.print(MAC); client.print(strNarodmon); client.println(" HTTP/1.1"); client.print("Host: "); client.printIn(server); client.println("Connection: close"); client.println(); client.println(); client,, stop (); client.flush() ; } else Serial.println("error send narodmon"); Serial.println(strNarodmon); // SerialEvent для НС05 void serialEventO() { while (HC05Serial.available()) { // получить очередной байт: char inChar = (char)HC05Serial.read(); // добавить в строку inputStringO += inChar; // /n - конец передачи if (inChar — f#f) { stringCompleteO = true; // парсинг строки из android void parse_stringO(String inputString) { // длина строки int lengthl=inputString.length();
Глава 18. Умная теплица 457 if(lengthl!=5) {Serial.printIn("ERR0R1"); return;} if(inputString.charAtB)!=f=f) {Serial.printIn("ERROR2"); return;} if(inputString.charAtD)! ='#f) {Serial.println("ERROR3"); return;} String paraml=inputString.substring@,2); int param2=inputString.substringC,4).tolnt(); Serial.print (ffparaml="); Serial.println (paraml); Serial.print("param2=");Serial.printIn(param2); if(paraml=="PM") doCoiranand B, min (param2,1)); else if(paraml=="FN") doCommand A, min (param2,1)); else if(paraml=="LM") doCommand @, min (param2,1)); // исполнение команды от смартфона void doCommand(int relay, int statusl) { // изменить статус statusRelays[relay]=statusl; // изменить состояние реле digitalWrite(pinRelays[relay],statusRelays[relay]); // запустить ethernet void ethernet_begin() { if(DHCP==0) { if(Ethernet.begin(arduinojnac)==0) Serial.println("dhcp - error"); else Serial.println("dhcp - ok"); } else { IPAddress arduino_ip (mip [0],mip [ 1 ],mip [2],mip [3]); IPAddress dns_ip (mdns [0],mdns [ 1 ] ,mdns [2] ,mdns [3]) ; IPAddres s gateway_ip(mgateway[0],mgateway[1],mgateway[2], mgateway[3]); IPAddress subnet_mask(mmask[O],mmask[l],rnmask[2],mmask[3]); Ethemet.begin(arduinojnac, arduino_ip, dns_ip, gateway_ip, subnetjnask);
458 Часть IV. Интересные проекты на Arduino // вывести ip-адрес void print_ip() { Serial.print("My IP address: "); for (byte thisByte = 0; thisByte < 4; thisByte++) { // print the value of each byte of the IP address: Serial.print(Ethernet.locallP0 [thisByte], DEC); Serial.printC1.") ; } Serial.println(); Serial.print("My SUBNET: ") ; for (byte thisByte = 0; thisByte < 4; thisByte++) { // print the value of each byte of the IP address: Serial.print(Ethernet.subnetMaskO [thisByte], DEC); Serial.print("."); } Serial.println(); Serial.print("My GATEWAYIP: "); for (byte thisByte = 0; thisByte < 4; thisByte++) { // print the value of each byte of the IP address: Serial.print(Ethernet.gatewayIP()[thisByte], DEC); Serial.print("."); } Serial.println(); Serial.print("My DNS: ") ; for (byte thisByte = 0; thisByte < 4; thisByte++) { // print the value of each byte of the IP address: Serial.print(Ethernet.dnsServerlPO [thisByte], DEC); Serial.print("."); } Serial.println(); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\18\_18_07 сопровождающего книгу электронного архива (см. приложение 2). После загрузки скетча в плату и отправки данных необходимо зарегистрироваться в сервисе «Народный мониторинг» и настроить визуализацию наших данных, а затем — добавить наше устройство на карту «Народного мониторинга». Для этого вводим МАС-адрес устройства: String MAC=5-75-02-FF-FF-01"; и нажимаем на кнопку ОК (рис. 18.27). Далее выбираем пункт меню Датчики | Настройка датчиков и триггеров, где изменяем названия датчиков и адрес местонахождения устройства (рис. 18.28). Более
Глава 18. Умная теплица 459 Рис. 18.27. Добавление устройства на карту сервиса «Народный мониторинг» Рис. 18.28. Установка названий для датчиков и адреса устройства
460 Часть IV. Интересные проекты на Arduino точно положение устройства на карте можно подстроить с помощью пункта меню Подвинуть по карте. Теперь вы можете увидеть на карте всплывающее окно с последними показаниями датчиков своей умной теплицы (рис. 18.29) и следить за ними из любого места, где есть доступ к сети Интернет. Рис. 18.29. Всплывающее окно с данными нашей теплицы на карте сайта «Народный мониторинг»
ГЛАВА 19 GPS-трекер и онлайн-сервис поиска стоянок В этой главе на основе модуля GPS-приемника, позволяющего определять наше местоположение с помощью глобальной системы GPS, мы создадим проект он- лайн-сервиса поиска стоянок, опирающегося на сервис Яндекс.Карты. 19.1. Подключение GPS-модуля к плате Arduino GPS (Global Positioning System) — это система, позволяющая с точностью до 100 м определить местоположение (координаты) объекта, то есть его широту, долготу и высоту над уровнем моря, а также направление и скорость его движения. Кроме того, с помощью GPS можно заффиксировать время с точностью до 1 не. GPS состоит из совокупности определенного количества искусственных спутников Земли (спутниковой системы NAVSTAR) и наземных станций слежения, объединенных в общую сеть. В качестве абонентского оборудования служат индивидуальные GPS-приемники, способные принимать сигналы со спутников и по принятой информации вычислять свое местоположение. В нашем проекте мы воспользуемся GPS-приемником (трекером) V.KEL VK16E (рис. 19.1). Рис. 19.1. GPS-приемник V.KEL VK16E
462 Часть IV. Интересные проекты на Arduino Так называемый холодный старт происходит, когда GPS-приемник был выключен длительное время, перемещался в выключенном состоянии на значительное расстояние, или его часы не совпадают с данными спутника. При холодном старте со спутников скачивается соответствующая информация, называемая альманахом. Время обновления альманаха — от 5 до 15 мин в зависимости от условий приема и количества видимых спутников. Необходимое условие— приемник в это время должен быть неподвижен. Теплый старт происходит, когда приемник был выключен более 30 мин. При этом прозводится прием уточняющих данных, занимающий 0,5-1,5 мин. Горячий старт происходит, когда приемник был отключен непродолжительное время. Данные на приемнике считаются свежими, и приемник просто находит спутники (опираясь на данные альманаха). Для проверки работоспособности подключим, используя адаптер USB-TTL, модуль по последовательному порту к компьютеру с ОС Windows и запустим программу MiniGPS_v1.7.1.exe. Программа показывает количество найденных приемником спутников и, в случае их достаточного числа, демонстрирует наше местоположение: географические широту и долготу. Мигание расположенного на модуле зеленого светодиода сигнализирует о достаточном для определения положения количестве спутников. Теперь подключим GPS-приемник к плате Arduino (рис. 19.2) и напишем скетч, выводящий данные о его местоположении: географические широту и долготу, а также текущую дату и время по Гринвичу — в монитор последовательного порта Arduino (рис. 19.3). При написании скетча (листинг 19.1) используются библиотеки SoftwareSerial и TinyGPS, позволяющие выделять нужные данные из всего потока, передаваемого приемником. Rx Рис. 19.2. Монтажная схема подключения GPS-приемника к плате Arduino
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 463 Рис. 19.3. Получение данных с GPS-модуля Электронный архив Библиотека TinyGPS размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). II подключение библиотек #include <SoftwareSerial.h> tinclude <TinyGPS.h> // создание экземпляров объектов TinyGPS gps; SoftwareSerial gpsSerialE, 6); bool newdata = false; unsigned long start; long lat, Ion; unsigned long time, date; void setup() { gpsSerial.begin(9600); // скорость обмена с GPS-приемником Serial.begin(9600); Serial.println("Waiting data of GPS..."); } void loop() { // задержка в секунду между обновлениями координат if (millisO - start > 1000) {' newdata = readgps(); if (newdata) { start = millis0; gps.get_position(&lat, &lon);
464 Часть IV. Интересные проекты на Arduino gps.getjdatetime(&date, &time); Serial.print("Lat: "); Serial.print(lat); Serial.print(" Long: "); Serial.print(Ion); Serial.print(" Date: "); Serial.print(date); Serial.print(" Time: "); Serial.printIn(time); } // проверка наличия данных bool readgps() { while (gpsSerial.available()) { int b = gpsSerial.read(); //в TinyGPS есть ошибка: не обрабатываются данные с \г и \п if C\r' !- b) { if (gps.encode(b)) return true; , return false; } Электронный архив Полный вариант рассмотренного скетча находится в папке examples\19\_19J)i сопровождающего книгу электронного архива (см. приложение 2). 19.2. Отправка данных по GPRS на сервер Arduino GPRS/GSM Shield предоставляет возможность использовать в Arduino- проектах сеть мобильной связи GSM для удаленного приема и передачи данных. Рассмотрим один из вариантов этого шилда — SIM900 Quad-Band GPRS shield на основе чипа SIM900 (рис. 19.4). Общение с платой производится через serial-соединение с помощью набора АТ-команд. Используя имеющиеся на плате перемычки, возможно установить задействуемые для коммуникации контакты: аппаратные 0-1, 2-3 (на некоторых платах) или 7-8 для работы через SoftwareSerial. Плату GSM GPRS SIM900 Shield можно выключить двумя способами: ? аппаратным (нажатием кнопки PWRKEY); ? программным. Для чего нужен программный сброс? Иногда при обмене по GPRS возникают ситуации, после которых модуль может зависнуть..Виной этому могут быть некорректные данные, пришедшие по сети и загнавшие в ступор SIM900, или помехи на линии обмена модуля и контроллера, или еще какие-нибудь неведомые проблемы. Производитель чипа предлагает в таких случаях перезагружать модуль с помощью
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 465 Рис. 19.4. GPS/GPRS shield: слева — вид сю стороны чипа SIM900; справа — вид со стороны монтажных выводов VBAT NRESET STATUS (OUTPUT) M*v20u$ Тур 60uS Min 12S Рис. 19.5. Программный сброс для GPS GPRS shield специальной последовательности импульсов, подаваемых на вход PWRKEY (рис. 19.5). Однако это не всегда помогает, и самым правильным способом перезагрузки модуля является полное снятие с него питания, выдержка некоторой паузы (хотя бы секунду — на всякий случай) и повторная подача питания. Для перезагрузки модуля на плате необходимо предусмотреть реле или транзисторный ключ, управляемый контроллером. По факту программный сброс через D9 не работал, поэтому пришлось подключаться перемычкой и управлять сбросом с цифрового выхода D4 платы Arduino. Схема соединений для этого случая показана на рис. 19.6.
466 Часть IV. Интересные проекты на Arduino 12В ЗА Рис. 19.6. Монтажная схема подключения GPS/GPRS shield к плате Arduino Рассмотрим управление модулем GSM/GPRS shield с пбмощью АТ-команд. Для этого установим модуль на плату Arduino и подключим ее к компьютеру. Arduino- скетч отправки и получения данных между компьютером и модулем GSM/GPRS shield через плату Arduino приведен в листинге 19.2. // подключение библиотеки #include <SoftwareSerial.h> // создание объекта SoftwareSerial grsG, 8); // RX, TX // скорость обмена #define GSMbaud 19200 String strl; char buff[100]; void setup() { Serial.begin(9600) ; gsm. begin(GSMbaud); Serial.println("Start");
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 467 void loop() { if (Serial.available()) { strl = Serial.readStringUntil(f\nf); strl.toCharArray(buffer, hh.length() + 1); gsm. write(buffer); gsm.board.write(f\nf); } if (gsm. available()) { Serial.write(gprs.read()); Данные с GPS-трекера мы будем отправлять на сервер по адресу PHP-скрипта, обрабатывающего GET-данные и записывающего их в базу данных: http://freeparkovka.ru/getkoords,php&id=xxx&lat=yyyyyyy&lon=zzzzzzzzz Для отправки данных нужно послать следующие АТ-команды: AT+SAPBR=1,1 // Открыть несущую (Carrier) // Тип подключения - GPRS AT+SAPBR=3,1,"CONTYPE","GPRS" // APN, для Билайна - internet AT+SAPBR=3,1,"APN","internet.beeline.ru" // Инициализировать HTTP AT+HTTPINIT // Carrier ID для использования. AT+HTTPPARA="CID",1 // Отправить данные методом GET AT+HTTPPARA="URL","http:/freeparkovka.ru/gps_trackerl.php?id=l&lat=XXXXXlon=YYY YY" AT+HTTPACTION=0 // Дождаться ответа AT+HTTPREAD // Остановить HTTP AT+HTTPTERM Пробуем отправить эту последовательность из монитора последовательного порта (рис. 19.7). Теперь напишем скетч (листинг 19.3). Функция sendATcoinmand () отправляет At-коман- ды и возвращает True в случае ожидаемого ответа и False при получении любого другого. boolean sendATcommand(char* ATcommand, char* expected_answer, unsigned int timeout) { uint8_t x=0; boolean answer=false;
468 Часть IV. Интересные проекты на Arduino char response[150]; unsigned long previous; memset(response, f\0f, 150); // Initialize the string delayA00); while( GPRSSerial.available() > 0) GPRSSerial.read(); // очистить буфер данных Serial.println(ATcoramand); GPRSSerial.println(ATcoiranand); // Send the AT command x = 0; previous = millisO; // ждем ответ do{ if(GPRSSerial.available() != 0) response[x] = GPRSSerial.read(); // сравнение на совпадение if (strstr(response, expected_answer) != NULL) { answer = true; // контролировать время ожидания while((answer == false) && ((millisO - previous) < timeout)); Serial.println(response); return answer; s^^^^^^^^^^^KgUE:mm bftfiCi I ~ :'~.h: шФпг: Рис. 19.7. Отправка АТ-команд на GPRS shield из последовательного порта
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 469 За отправку группы АТ-команд отвечают функции: ? iniGPRS () — инициализация GPRS и подключение к сети оператора; ? sendkoords () — инициализация HTTP и отправка данных серверу; ? resetGPRS () — программный сброс; ? onof f gprs () — перезагрузка модуля выключением/включением питания. Содержимое основной части скетча показано в листинге 19.4. // подключение библиотеки SoftwareSerial tinclude <SoftwareSerial.h> //Подключение дисплея lsdl602 i2c #include <Wire.h> #include <LiquidCrystal_I2C.h> //Подключение библиотеки для gps #include <TinyGPS.h> // Создание объектов библиотек TinyGPS gps; SoftwareSerial SerialGpsE, 6); SoftwareSerial GPRSSerialG, 8); // RX, TX //Служебные переменные unsigned long startGPS; bool newDataGps=false; unsigned long millisGPRS; unsigned long millisGPS; float lat=44.029678; float lon=43.072151; // кол-во ошибок int errorl=0; int error2=0; int error3=0; void setup() { GPRSSerial.beginA9200); Serial.begin(9600); delayC000); Serial.printIn("iniGPRS - start ") ; while(!iniGPRS()) { error2++; if(error2>3) { while(!resetGPRS()) { errorl++;
470 Часть IV. Интересные проекты на Arduino if(errorl>3) { onoffGPRS() ; errorl=0; error2=0; Serial.printIn("iniGPRS - ok "); void loop() { // получение каждые 5 с if (millisO-millisGPS >= 3000) GPRSSerial.endO; SerialGps.begin(9600); getGPS(); millisGPS=millis() ; // отправка каждые 10 с if (millisO-millisGPRS >= 10000) SerialGps.end(); GPRSSerial.beginA9200); if(!sendkoords()) { if(error3>3) { while(!iniGPRS()) { error2++; if(error2>3) { while (IresetGPRSO) { errorl++; if(errorl>3) { onoffGPRS() ; errorl=0; error2=0; } } error3=0; millisGPRS=millis();
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок Электронный архив Полный вариант рассмотренного скетча находится в папке examples\19\_19_04 сопрово- ждающего книгу электронного архива (см. приложение 2). На стороне сервера данные принимает РНР-скригтг getkoords.php. Содержимое этого РНР- скрипта показано в листинге 19.5. Скрипт получает данные и записывает их в таблицу базы данных users (рис. 19.8). Рис. 19.8. Запись в таблицу базы данных <?php . $location="localhost"; $user="bhx20666_parking"; $pass="****************"; $db_name="bhx20666j?arking"; // if (! $db=imysqli_connect ($location/ $userf $pass, $db_name)) {echo "connect error";} else mysqli_query($db/"SET CHARACTER SET futf8ffl); $queryO=" SELECT * FROM Users WHERE id=".$_POST[iduser]." "; $rezO=mysqli_query($db,$queryO);
472 Часть IV. Интересные проекты на Arduino $rowO=mysqli_fetch_assoc ($rezO); $str=$rowO[lat].";".$rowO[lon]."; ff.$row0[last_time].";"; echo $str; 19.3. Создание веб-страницы с использованием API Яндекс.Карт Теперь остается купить хостинг, доменное имя и написать код страницы для отображения текущего местоположения GPS-трекера. JavaScript API поможет встроить на сайт или в приложение карту поиска по топонимам и организациям с возможностью строить маршруты и смотреть панорамы, а также с другими функциями, доступными на Яндекс.Картах. С помощью JavaScript API вы можете настроить нужную логику взаимодействия пользователя с картой и определить, как эта карта будет выглядеть. Чтобы задать внешний вид объектов на карте, можно выбрать стандартные элементы или создать собственный макет. Пользоваться API Яндекс.Карт можно бесплатно, если соблюдать условия: ? все данные должны отображаться на карте, размещенной на общедоступном сайте или в приложении. Сохранять или изменять данные нельзя, но можно кэшировать запросы к геокодеру и маршрутизатору на срок до 30 дней; ? бесплатный API нельзя использовать для мониторинга транспорта и в закрытых системах; ? общее число запросов к геокодеру, маршрутизатору и панорамам в сутки не должно превышать 25 тыс. Также есть другие ограничения на бесплатное использование. Подробнее о них можно прочитать в документации. Для использования API Яндекс.Карт на своем сайте необходимо получить API- ключ (рис. 19.9). Для использования на странице API необходимо подключить библиотеку— разместить в заголовке страницы следующий код: <script src="https://api-maps.yandex.ru/2.l/?apikey=75e85ef2-7142-428b-8bd0- 785b0ba84f55&lang==ru_RU" type="text/javascript"></script> Теперь мы можем использовать возможности библиотеки, создавать карту и размещать на ней объекты (рис. 19.10). Код javascript создания карты: myMap = new ymaps.MapC'map", { center: [44.036578, 43.074971], zoom: 12
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 473 Рис. 19.9. Ключи API Рис. 19.10. Страница с картой на сайте
474 Часть IV. Интересные проекты на Arduino Создается коллекция объектов: myGeoObjects = new ymaps.GeoObjectCollection({}, { preset: "islands#blueCircleIcon", strokeWidth: 4, geodesic: true }); в которую заносим метки для всех автостоянок— данные берем из базы mysql (рис. 19.11). Код создания метки, координат, содержимого балуна формируется PHP-скриптом. Скриптом формируется и список всех автостоянок в таблице. Рис. 19.11. Данные об автостоянках в базе данных Также создается метка для нашего GPS-трекера (РНР-код): <?php $queryl="SELECT * FROM Users WHERE id=l "; $rezl=mysqli_query ($db, $queryl); $rowl=mysqli_fetch_assoc ($rezl); $str="AvtoPlacemark = new ymaps.Placemark([".$rowl[lat].",".$rowl[Ion]."], {balloonContentBody: 'Последние показания <br>".$rowl[last_time]."', hintContent: f".$rowl[last_time]."f}, {iconLayout: f default#image', iconlmageHref: f iconavto.jpgf, iconlmageSize: [40, 40], iconlmageOffset: [-20, -20]});"; echo $str;
Глава 19. GPS-трекер и онлайн-сервис поиска стоянок 475 Для обновления информации о положении GPS-трекера, изменении информации и свободных местах на стоянках, отправки запроса бронирования используется технология Ajax: <script src="js/jquery-3.3.1.min.js"x/script> <script type="text/javascript"> function getKoordsAvto() { $.ajax({ type: fPOSTf, url: 'getkoords.php', cache: false, data: {"iduser": 1 }, success: function (data) { var arr=data.split(";"); AvtoPlacemark.geometry.setCoordinates([arr[0],arr[l]]); setTimeout(getKoordsAvto,10000); </script> При нажатии кнопки Забронировать отправляется запрос на сервер для перенаправления запроса на автостоянки и на карте строится маршрут (рис. 19.12). Рис. 19.12. Построение маршрута
ГЛАВА 20 Проекты для вендинга: купюроприемник, монетоприемник, разменный автомат Продажа товаров и услуг с помощью автоматизированных систем (торговых автоматов) называется вендингом. Вендинг получил широкое распространение в мире как удобный и не очень требовательный способ вести торговлю или оказывать услуги. На рынке представлен большой выбор торговых автоматов. Но если вас не устраивает цена, или вы желаете добавить функционала действующему автомату, или решили собрать что-то новенькое — Arduino в качестве микроконтроллера для управления отдельными блоками устройства вам в этом поможет. Рассмотрим работу Arduino с купюроприемником, монетоприемником и хоппером. Купюроприемник (он же банкнотник, кугаорник, валидатор, биллакцептор) — устройство, предназначенное для приема наличных платежей банкнотами. Купюро- приемники осуществляют прием купюр, распознавание номинала и (при наличии механизма укладки и кассеты) хранение принятых купюр. Монетоприемник — устройство, предназначенное для приема платежей монетами или жетонами. Хоппер — устройство для выдачи сдачи монетами. Купюроприемники, монетоприемники и хопперы устанавливают в платежные терминалы, развлекательные, лотерейные и игровые автоматы, а также в вендинговые (торговые) автоматы. 20.1. Купюроприемник ЮТ серий А7 и V7 Купюроприемники ICT серий А7 и V7 (MDB-версия) по-настоящему проверены временем — на российском рынке эта модель появилась еще в 2003 г. и с успехом эксплуатируется до сих пор как в индустрии игорного бизнеса, так и в вендинге и системах моментальных платежей (рис. 20.1). Модель поставляется с механизмом укладки на 200, 400 и 800 купюр и предоставляет возможность быстрой замены микропрограммы с помощью ПК. Основные возможности: ? осуществляет прием купюры, поданной в любом направлении; ? уровень распознавания купюр — более 96%;
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 477 Рис. 20.1. Купюроприемник ICTV7 ? время операции — 3 с (включая время укладки); ? обеспечивает работу по протоколу PULSE, RS 232, MDB; ? имеет контейнер укладчика на 300 купюр; ? напряжение питания — 12 В; ? позволяет обновлять микропрограмму в Flash Rom; П имеется модель с горизонтальным механизмом укладки. Немаловажный факт — в Интернете можно приобрести работающие б/у экземпляры по цене до 1000 рублей (лично я приобрел два у разных продавцоз). Для настройки купюроприемник имеет три блока переключателей: первый и второй блоки переключателей находятся с боковой стороны купюроприемника (рис. 20.2), третий блок спрятан: разворачиваем купюроприемник к себе лицевой стороной и кассетой вверх, над лицевой панелью находим пластиковую крышку — чтобы она открылась, достаточно потянуть ее немного вверх, — вы увидите плату и, поискав, найдете и колодку с четырьмя переключателями (рис. 20.3). Каждый переключатель (в просторечии «дип») имеет два положения: on — вниз и off— вверх. Рассмотрим назначение переключателей: ? первый блок: • sw 1-5 — настройка приема по номиналам купюр; • sw 6 — качество приема. Если установить HighAcceptance, то купюроприемник принимает даже помятые купюры;
478 Часть IV. Интересные проекты на Arduino • sw7 — отключает прием inhibit (сигнал, устанавливающий блокировку приема купюр); • sw 8 — настройка уровня сигнала inhibit; ? второй блок: • sw 1-2 — настройка количества импульсов; • sw 3-4 — настройка длины импульса и длины паузы. Сводная таблица настройки переключателей первого и второго блоков представлена в табл. 20.1; Рис. 20.2. Купюроприемник ICT V7: слева — вид спереди; справа — вид сбоку; вверху — увеличенное изображение первого (слева) и второго (справа) блоков переключателей
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 479 О третий блок (который под крышкой): • SW 1 ПОЛЯРНОСТЬ ИМПуЛЬСа High (on) ИЛИ Low (off); • sw 2 — протокол pulse (on) или rs232 (off); • sw 3-4 — не используются. Чтобы любые изменения вступили в силу, купюроприемник надо перезагрузить. Рис. 20.3. Купюроприемник ICT V7, вид спереди: слева — крышка закрыта; справа — крышка открыта; вверху — увеличенное изображение третьего блока переключателей
480 Часть IV. Интересные проекты на Arduino Таблица 20.1. Таблица настройки переключателей первого и второго блоков Функция Reject Ruble 10 Accept Ruble 10 Reject Ruble 50 Accept Ruble 50 Reject Ruble 100 .Accept Ruble 100 Reject Ruble 500 Accept Ruble 500 Reject Ruble 1000 Accept Ruble 1000 High Acceptance High Security Harness disable Harness enable Inhibit Active High Inhibit Active Low 1 pulse/Ruble 10 2 pulse/Ruble 10 5 pulse/Ruble 10 20 pulse/Ruble 10 50 ms on/50 ms off 60 ms on/300 ms off 30ms.on/50msoff 150 ms on/150 ms off Переключатели первого блока 1 on off 2 on off 3 on off 4 on off 5 on off 6 on off 7 on off 8 on off Переключатели второго блока 1 off on off on 2 off off on on 3 off on off on 4 off off on on 20.1.1. Подключение купюроприемника ICT V7 к плате Arduino Купюроприемник оснащен кабелем для подключения к автомату, на конце кабеля имеется разъем 3x3 (рис. 20.4). Назначение интересующих нас выводов следующее: ? красный — 12 В; ? оранжевый — GND;
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 481 П желтый — inhibit*; ? зеленый — inhibit-; ? синий — signal+; ? фиОЛеТОВЫЙ SIGNAL-. Рис. 20.4. Кабель подключения купюроприемника ICT V7 Подключаем купюроприемник к плате Arduino по протоколу pulse. Схема подключения приведена на рис. 20.5, а установка переключателей — в табл. 20.2. Такое их положение определяет режим pulse с полярностью High, 1 импульс на 10 рублей E— на 50 рублей, 10— на 100 рублей), длительность импульса 50 мс, качество приема низкое, отсутствие сигнала блокировки купюроприемника и прием купюр номиналом 10, 50 и 100 рублей с отторжением 500- и 1000-рублевых купюр. Таблица 20.2. Таблица установки переключателей Первый блок 1 off 2 off 3 off 4 on 5 on 6 off 7 off 8 off Второй блок 1 off 2 off 3 off 4 off Третий блок 1 on 2 on 3 off 4 off +12 V ICV7 красный +12В оранжевый GND синий SIGNAL+ — ММ R1 - I1 i <- i i 4,7К — Arduino ТХХ АО D2 А1 D3 А2 D4 A3 D5 А4 D6 А5 D7 А6 D8 А7 по иу D10 D11 +З.ЗВ D12 D13 +5В GND 1 ± — — - - Рис. 20.5. Схема подключения купюроприемника ICT V7 к плате Arduino
482 Часть IV. Интересные проекты на Arduino 20.1.2. Скетч для получения номинала принимаемой купюры Выход D2 платы Arduino подсоединен к выходу signal* купюроприемника и установлен в режим получения данных (input). Через резистор 4,7 кОм (см. рис. 20.5) он подтянут к питанию +5 В, и на нем находится уровень high. После получения купюры купюроприемник посылает импульсы продолжительностью 50 мс на выход signal+, устанавливая на входе D2 уровень low и вызывая процедуру обработки прерывания, где инкрементируется счетчик количества импульсов. В основном цикле программы (листинг 20.1) проверяется время, прошедшее после получения первого импульса, и если оно превысило 1000 мс, выводится номинал полученной купюры (на основе количества импульсов), а счетчик обнуляется до поступления первого импульса при приеме новой купюры. const int moneyPin=2; // подключение SIGNAL+ int money=0; // номинал принятой купюры unsigned long timeAllPulse=2000; // макс, время приема купюры unsigned long timeCount=0; void setup() { Serial.begin(9600); pinMode(moneyPin,INPUT); attachlnterrupt@,count_pulse,FALLING); Serial.printIn("ready"); void loop() { // прошло максимальное время приема купюры? - вывести номинал if (money>0 && (millis () -timeCount) >timeAllPulse) { Serial.print("money="); Serial.printInA0*money); money=0; // обработка прерывания - получить кол-во импульсов // void countjpulse () { detachlnterrupt@); money++;
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 483 if (money==l) timeCount=:rtiillis (); attachlnterrupt @, count_pulse, FALLING) ; } Электронный архив Полный вариант рассмотренного скетча находится в папке examples\20\_20JI сопровождающего книгу электронного архива (см. приложение 2). 20.2. Монетоприемник СН-926 Монетоприемник — это устройство для приема монет (жетонов), которые используются во всех видах торговых автоматов, сенсорных киосках, платежных терминалах, автономных устройствах услуг. Их цель — принять и распознать металлические деньги (монеты, жетоны). Монетоприемники можно разделить на четыре вида: ? монетоприемники эталонного типа (компараторы) — принимающие монеты или жетоны только одного типа; ? программируемые монетоприемники— способные распознавать монеты различных номиналов; ? монетоприемники-селекторы; О монетоприемники с функцией выдачи сдачи. Программируемые монетоприемники используются во всех торговых и платежных терминалах. Для игровых или развлекательных автоматов достаточно компаратор- ных монетников (эталонного типа). В зависимости от наличия свободного места внутри самого аппарата, монетоприемники устанавливаются сбоку (пристраивается дополнительный бокс) или за фасадной панелью автомата. Монета (жетон) попадает в монетоприемник, проверяется там на предмет подлинности (в современных устройствах сверяются до 24 параметров), и далее, в случае одобрения, аппарат получает команду выдать товар или произвести какую-либо операцию. Новые модели монетоприемников благодаря встроенным оптическим датчикам исключают такие распространенные виды мошенничества, как монета (жетон) на нитке. Выбор монетоприемников как дополнительного оборудования достаточно широк, что дает возможность исходя из конкретных задач подобрать оптимальную модель. Рассмотрим программируемый монетоприемник СН-926 (рис. 20.6), который можно приобрести на ЕЬау по достаточно приемлемой цене (дешевле 1000 рублей). Он может принять до 6 видов различных монет диаметром от 15 до 32 мм и толщиной от 1,2 до 3,8 мм. Рабочее напряжение— 12 В. Выходной сигнал— импульсный. Частота импульсов устанавливается с помощью трехпозиционного переключателя: fast — 20 мс, medium — 50 мс, slow — 100 мс.
484 Часть IV. Интересные проекты на Arduino Рис. 20.6. Монетоприемник СН-926 20.2.1. Настройка монетоприемника Рассмотрим процесс программирования (настройки) монетоприемника СН-926: 1. Включить питание, установить переключатель 1 в позицию NC. 2. Переключателем 2 выбрать нужную скорость обмена (fast, medium, slow). 3. Нажать одновременно кнопки ADD и MINUS и удерживать их более 3 с, отпустить — на индикаторе появится А. 4. Нажать и отпустить кнопку SET — на индикаторе появится Е. 5. Кнопками ADD и MINUS установить количество разных монет для приема: от 1 до 6 (я установил 5 — для настройки монетоприемника на прием 50 копеек, 1,2, 5 и 10 рублей). 6. Нажать кнопку SET более 3 с — на индикаторе появится HI (количество экземпляров первой монеты для калибровки). 7. Кнопками ADD и MINUS установить значение Н для первой монеты.
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 485 8. Нажать кнопку SET более 3 с — на индикаторе появится Р1 (количество выдаваемых импульсов при успешном приеме первой монеты) — выбирается от 1 до 50. 9. Кнопками ADD, MINUS установить значение импульсов для первой монеты (я выбрал 1 — для первой, 2 — для второй, 3 — для третьей, 4 — для четвертой, 5 — для пятой); 10. Нажать кнопку SET более 3 с — на индикаторе появится F1 (точность опознания первой монеты) — выбирается от 1 до 30 (я выбрал 10). 11. Кнопками ADD и MINUS установить значение F для первой монеты. 12. Нажать кнопку SET более 3 с. 13. Повторить шаги 6-12 для других выбранных монет. По окончании на индикаторе появится А. 14. Нажать и отпустить кнопку SET — на индикаторе появится Е. Теперь выключаем/включаем монетоприемник и проводим его калибровку. 20.2.2. Калибровка монетоприемника Процесс калибровки монетоприемника СН-926 заключается в следующем: 1. Нажать кнопку SET более 3 с — на индикаторе появится А1. 2. Начинаем опускать в монетоприемник монеты номинала 1 в количестве HI. 3. По загрузке последней монеты на индикаторе начинает мигать А1. 4. Нажать кнопку SET — на индикаторе появится А2. 5. Повторить загрузку монет для номиналов 2-5. 6. Нажать кнопку SET более 3 с. После включения/выключения монетоприемник готов к приему монет. Мы можем опускать в него монеты— на индикаторе высвечивается количество импульсов. Если монеты какого-либо номинала не проходят, повторяем настройку сначала. 20.3. Разменный автомат (хоппер) Cube Hopper MK II Хоппер Cube Hopper MK II (рис. 20.7) при внесении купюры через купюроприемник осуществляет ее размен на монеты, а также хранит и выдает сдачу монетами одного номинала — это один из самых распространенных хопперов, он разработан всемирно известной компанией Suzo, известнейшим игроком на рынке развлекательного оборудования. Модель Cube Hopper MK II специально предназначена для игровой и вендинговой индустрии, выполнена из износостойкого пластика и обладает высокой надежностью.
486 Часть IV. Интересные проекты на Arduino Характеристики хоппера Cube Hopper MKII: О количество номиналов: 1; ? есть варианты под монеты достоинством 1, 2, 5 и 10 рублей; ? скорость выдачи монет: 7 монет/с; ? размер монет: 18-31 мм; ? толщина монет: 1,5-3,20 мм; ? питание: 12 В постоянного тока (DC) ±10% или 24 В постоянного тока от -25 до+10%; О температура окружающей среды: от 0 до 50°С; ? вместимость: 450 монет диаметром 24 мм (опционально в устройство допускается установить до трех расширителей держателя монет, что дает возможность загружать в его бункер более 1200 монет (жетонов) диаметром 24 мм); ? современное электронное управление оптическими датчиками выдачи; ? работает в двух протоколах: ccTalk или параллельный; ? имеет квадратный форм-фактор — выдача монет (жетонов) может быть осуществлена с 4 различных сторон в зависимости от необходимости. Рис. 20.7. Хоппер Cube Hopper MK II
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 487 20.3.1. Подключение хоппера к плате Arduino Для подключения к плате Arduino используются следующие контакты на разъеме хоппера: ? pin 6 — выход оптического сенсора; ? pin 8 —+12 В; ? pin 9 — «земдя». При подаче на pin 8 напряжения +12 В хоппер начинает выдавать монеты. Выдав каждую монету, хоппер подает на pin 6 отрицательный импульс длительностью 35 мс. При подключении ко входу платы Arduino pin 6 хоппера подтягивается резистором 10 кОм к контакту +5 В. Монтажная схема соединений разменного автомата в составе купюроприемника и хоппера показана на рис. 20.8. 20.3.2. Программирование хоппера Как уже отмечалось, при внесении купюры через купюроприемник хоппер производит размен ее на монеты. Если в нем недостаточно для этого монет, прием денег купюроприемником блокируется. Содержимое скетча, обеспечивающего эту функциональность, представлено в листинге 20.2. Загружаем скетч в плату Arduino и проверяем его работу. // пин включения/выключения купюроприемника #define PIN_KUP_ON 12 // пин включения/выключения хоппера #define PIN_HOPPER_ON 11 // пин сигнала хоппера #define PIN_HOPPER_S 4 // пин сигнала хоппера #define PIN_BLOCK 9 #define KUP_ON 0 #define KUP_OFF 1 #define HOPPER_ON 1 #define HOPPER_OFF 0 // время ожидания монеты в хоппере (хоппер пустой) #define TIME_WAIT_HOPPER 5000 // для хоппера поправка #define TIME1_MAX 200 // адреса EEPROM для хранения // блокировано? #define ADDR_BLOCK 27 // для EEPROM tinclude <EEPROM.h>
488 Часть IV. Интересные проекты на Arduino I I о. ё
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 489 II купюроприемник const int moneyPin2=2; int pulse2=0; // макс, время ожидания импульса после приема купюры unsigned long timeAHPulse2=500; unsigned long timeCount2=0; unsigned int sum=0; // параметры номинала монеты - 10 рублей unsigned int nominal=10; // блокировка приема int block=0; void setup() { Serial.begin(9600); block=EEPRQM.read(ADDRJBLOCK); pinMode(PIN_KUP_ON, OUTPUT); // проверка отсутствия монет if(digitalRead(PIN_BLOCK)==0 && block==l) digitalWrite(PIN_KUP_ON,KUP_OFF); else digitalWrite(PIN_KUP_ON,KUP_ON) ; pinMode(PIN_HOPPER_ON,OUTPUT); digitalWrite(PIN_HOPPER_ON,HOPPER_OFF); attachlnterrupt@,count_pulse2,FALLING); void loop() { // прошло максимальное время ожидания импульса приема купюры? if(pulse2>0 && (millis()-timeCount2)>timeAHPulse2 ) { sum=sum+pulse2* 10; Serial.print("sumpulse2=");Serial.println(pulse2*10); i f(sum>=nominal) { Serial.print("sum=");Serial.println(sum); // блокировка купюроприемника digitalWrite(PIN_KUP_ON,KUP_OFF); detachlnterrupt@); // выдача монет if(exchange(sum)) { // успешно digitalWrite(PIN_KUP_ON,KUP_ON); attachlnterrupt@,count_pulse2f FALLING); } else { if (digitalRead (PIN_BLOCK) =1) { digitalWrite (PIN_KUP_ONfKUP_ON) ; attachlnterrupt@,count_pulse2, FALLING);
490 Часть IV. Интересные проекты на Arduino Ыоск-1; } Serial.print("sum=");Serial.println(sum) ; EEPRQM.write(ADDR_BLOCK,block) ; // обработка прерывания купюроприемника - получить кол-во импульсов void countjpulse2() { detachlnterrupt@); pulse2++; if(pulse2==l) timeCount2=-millis (); delayA0); attachlnterrupt@,count_pulse2,FALLING); //attachlnterrupt(lf count_pulse2,CHANGE); } // выдача размена boolean exchange (int summa) { boolean lastSignal=HIGH; boolean currentsignal=HIGH; unsigned long millisl=0; int count; unsigned int timel=0; timel=TIMEl_MAX; count=sum/nominal; int pulse3=0; unsigned long millis2=millis(); digitalWrite(PIN_HOPPER_ONf HOPPER_ON); while(pulse3<count) { currentSignal=debounce(lastSignal,PIN_HOPPER_S); if(lastSignal==HIGH && currentSignal==LOW) { pulse3++; Serial.println(pulse3); millisl=millis(); //delayD0); } lastSignal=currentSignal; if (millis () -millisl> TIME_WAIT_HOPPER) break; } delay (timel) ; digitalWrite(PIN_HOPPER_ON,HOPPER_OFF); delayA000);
Глава 20. Проекты для вендинга: купюроприемник, монетоприемник... 491 sum=sum-pulse3*nominal ; if(pulse3<count) return false; else return true; /* Функция сглаживания дребезга * Принимает в качестве аргумента предыдущее состояние кнопки * и выдает фактическое. V int debounce(int last,int pinl) { int current = digitalRead(pinl); // Считать состояние кнопки if (last != current) // если изменилось... { delayE); // ждем 5 мс current = digitalRead(pinl); // считываем состояние кнопки return current; // возвращаем состояние кнопки Электронный архив Полный вариант рассмотренного скетча находится в папке examples\20\_20JJ сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 21 Создание управляющей платы для автомойки самообслуживания В коммерческих проектах для включения какого-нибудь устройства на определенное время, зависящее от поступившей оплаты, — например, в популярных автомойках самообслуживания, также используются купюроприемники и монетоприемники, рассмотренные нами в главе 20. Автоматика мойки самообслуживания состоит из блока приема денежных средств (купюроприемник, монетоприемник), блока отображения информации, нескольких кнопок и нескольких реле времени обратного счета с различными временами отсчета в зависимости от выбранной кнопки запуска, к которым подключается оборудование для выполнения определенных функций— например: мойка водой под давлением, активная пена, воск, осмос (ополаскиватель). Рассмотрим создание на Arduino контроллера (управляющей платы) автомойки самообслуживания. 21.1. Блок приема денежных средств и блок индикации К плате Arduino по схеме, показанной на рис. 21.1, подключены купюроприемник и монетоприемник. Вывод информации о принятых средствах осуществляется на четырехразрядную матрицу 8><8 (см.разд. 10.6). Получаемые денежные средства накапливаются в переменной sum. Для вывода суммы на дисплей D-разрядную матрицу 8><8) необходимо создать массивы для изображения цифр и других дополнительных символов: byte figure[25][10]={ {8,8, //0 В00111110, В01111111, В01100011, В01100011, В01100011, В01100011, В01111111, В00111110},
Глава 21. Создание управляющей платы для автомойки самообслуживания 493 I >s О I § Я I I >s s i i
494 Часть IV. Интересные проекты на Arduino {8,8, воооооооо, воооооооо, воооооооо, воооооооо, воооооооо, воооооооо, воооооооо, ВОООООООО}, {8,8, В11100111, В11000011, В10000001, В00011000, В00100100, В11111111, В11111111, В01100110}, {8,8, В11100000, В10100000, В10100000, В10101110, В10101010, В00001010, В00001010, В00001010}, //пустота {8,8, В01100011, В01100011, BOiiioon, В01101011, В01100111, В01100011, В01100011, В01100011}, //для мигания //прогр. пауза //буква N Для работы с дисплеем D-разрядной матрицей 8><8) используем библиотеку MaxMatrix: #include <SPI.h> #include <MaxMatrix.h> MaxMatrix m(ll,10,13,4); // инициализация матриц m.init (); m.setlntensityA5);
Глава 21. Создание управляющей платы для автомойки самообслуживания 495 и создаем функцию вывода цифр на разряды дисплея: // показать цифры на матрицах void view_figure(unsigned int num,int mode) { int numl,num2,num3,num4; numl=num/1000; num2=(num-numl*1000)/100; num3=(num-numl*1000-num2*100)/10; num4=(num-numl*1000-num2*100-num3*10)/1; m.writeSpriteB4, 0, figure[num4]); if(num3==0 && nunKlO) m.writeSpriteA6, 0, figure[10]); else m.writeSpriteA6, 0, figure[num3]); if(num2==0 && nuirKlOO) m.writeSprite(8, 0, figure[10]); else m.writeSprite (8, 0, figure [num2]); if(numl==0 && num<1000) m.writeSprite@, 0, figure[10]); else m.writeSprite@, 0, figure[numl]); if (numl=0) m.writeSprite@, 0, figure[mode]); Электронный архив Библиотека MaxMatrix размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). При приеме монет и купюр считаем в процедурах прерывания импульсы и переводим их в сумму. А в основном цикле loop () выводим накапливаемую сумму на дисплей: // обработка прерывания купюроприемника - получить кол-во импульсов void count_pulse2() { detachlnterruptA); pulse2++; if(pulse2==l) timeCount2=millis(); delayA0); attachlnterruptA,count_pulse2,FALLING);
496 Часть IV. Интересные проекты на Arduino void loop () { // прошло максимальное время приема купюры? if(pulse2>0 && (itiillis()-timeCount2)>timeAHPulse2) { sum=sum+nominals2 [min (pulse2,15) ] ; pulse2=0; view_figure(sum, 10) ; 21.2. Выбор программ работы мойки Для выбора программ мойки служит блок кнопок и реле для включения выбранных функций. Мы воспользуемся антивандальными кнопками с подсветкой (рис. 21.2). Блок кнопок состоит из 6 кнопок: выбор программ (кнопки 1-5) и кнопка для установки паузы. Схема подключения компонентов показана на рис. 21.3. Рис. 21.2. Антивандальная кнопка с лампочкой на 12 В В цикле loop о постоянно идет процесс проверки нажатия кнопок с учетом возможности «дребезга»: int pinButtons2[]={14,15,16f17,18,19}; // 14 - пауза int lastButtons2[] = {0,0,0, 0,0,0,0,0}; int currentButtons2[]={0,0,0,0,0,0,0,0}; void loopO {
§ I I Рис. 21.3. Схема подключения кнопок с подсветкой и реле
498 Часть IV. Интересные проекты на Arduino // проверка нажатия кнопок выбора программ for(int i=0;i<COUNT_BUTTONS;i currentButtons2[i] = debounce(lastButtons2[i],pinButtons2[i]); // если нажатие... if (lastButtons2[i] = 0 && currentButtons2[i] — 1) { doButtons(i); } lastButtons2[i] = currentButtons2[i]; // обработка клавиш выбора программ void doButtons(int but) { if(sum<l) return; if (but—0) { RUN1.prg=8;RUN1.stopprg=l; RUNl.millispausel=*nillis (); RUN1.speedmoney=500; } else { RUN1.prg=but;RUN1.stopprg=0; RUN1.speedmoney=60000/(unsigned long)params[but]; millisttt^millis(); } setledsrelays(RUNl.prg); view_figure(sum,10); } Определив нажатие кнопки, запускаем необходимые для выбранной программы реле. Список состояний реле берем из массива relays (о — включить, 1 — выключить). Поскольку выводы подсветки кнопок включены параллельно управляющим контактам реле, одновременно загораются и нужные кнопки: int pinRelays2[] = {12,8,7, 6,5,4}; byte relays[9][б]={{1,1,1,1,1,1}, {0,1,1,1,1,1}, {1,0,1,1,1,1}, {1,1, 0,1,1,1}, {1,1,1,0,1,1},{1,1,1,1,0,1}, {1,1,1,1,1,1},{0,0,0,0,0,0}, {1,1,1,1,1,0}}; // установка подсветки кнопок включения реле void setledsrelays(byte valuel)
Глава 21. Создание управляющей платы для автомойки самообслуживания 499 int w=(int) valuel; for(int j=0;j<=COUNT_LEDS;j++) digitalWrite (pinRelays2 [ j ], relays [w] [j ]); 21.3. Отсчет времени выполнения программы. Реализация паузы Все параметры выполнения выбранной программы содержатся в переменной runi: struct RUN // структура { int modeadmin; // режим работы/установка параметров int prg; // номер выполняемой программы int stopprg; // выключена программа - пауза long millispausel; // начало паузы int view; // для мигания при паузе unsigned long speedmoney; // скорость ухода денег }; RUN RUNl={0, 0, 0,0,0,500} ; При выборе программы переменная runi заполняется параметрами этой программы. Параметры хранятся в памяти EEPROM. В цикле loop о выполняется декремент баланса на одну единицу каждые runi . speedmoney мс. Если нажата кнопка паузы, происходит отсчет максимального времени паузы, а при его превышении возобновляется последняя программа. Если во время паузы происходит выбор программы, начинается ее выполнение. Во время паузы на дисплее мигает значение баланса. Содержимое цикла loop о показано в листинге 21.1. void loop () { // проверка нажатия кнопок выбора программ for(int i=0;i<COUNTBUTTONS;i currentButtons2[i] - debounce(lastButtons2[i],pinButtons2[i]); if (lastButtons2[i] = 0 && currentButtons2[i] == 1) // если нажатие... { doButtons(i); lastButtons2[i] = currentButtons2[i]; } // режим пользователя if (millis()-millisttt>=RUNl.speedmoney)
500 Часть IV. Интересные проекты на Arduino millisttt=millis(); // идет программа if(RUNl.prg>0 && RUNl.stopprg<l) { sum=sum-l; view_f igure (sum, 10); if(sum<l) {sum=0;RUNl.prg=0; ' RUN1.speedmoney=1000; setledsrelays@); //view_figure(sum,11); delay(MILLISBOUNCE); pulse2=0;pulsel=0; m.init () ; m.setlntensityA5); view_figure(sum,11); // пауза else if (RUNl.prg>0 && RUNl.stopprg=l) { RUNl.view=8-RUNl.view; if(RUNl.view==0) view_figurel@) ; else view_f igure (sum, 10); millisppp=params[0];millisppp=millisppp*1000; if (millis () -RUNl .millispausel>millisppp) { sum=0;RUNl.prg=0; RUNl.speedmoney=500; view_f igure(sum,11); setledsrelays@); } else if(RUNl.prg==0 && RUNl.stopprg==2) { if(millis()-RUNl.millispausel>millisppp) { sum=0;RUNl.prg=0; RUNl.speedmoney=500; view_figure(sum, 11); setledsrelays@); Serial.print("*53;0;$");
Глава 21. Создание управляющей платы для автомойки самообслуживания 501 else ; } // прошло максимальное время приема купюры? if(pulse2>0 && (millis()-tiitieCount2)>timeAHPulse2) { if(sum==0) RUN1.stopprg=2; suin=sum+nominals2 [min (pulse2/15) j ; pulse2=0; view_figure(sum,10) ; RUN1 .millispausel=inillis () ; millisppp=params[0];millisppp==millisppp*1000; // прошло максимальное время приема монеты? if(pulsel>0 && (millis()-timeCountl)>timeAllPulsel) { if(sum==0) RUNl.stopprg=2; sum=sum+nominalsl [min(pulsel, 15) ] ; pulse1=0; view_figure(sum,10); RUN1.millispausel^millis(); millisppp=params[0];millisppp=millisppp*1000; } // первоначальный ввод денег if(sum>0 && RUNl.prg==0) { view_figure(sum,10) ; setledsrelays@); 21.4. Режим администратора. Установка лараметров Режим администратора позволяет установить «стоимость» каждой программы и длительность паузы. Для выбора режима администратора необходимо на контакт Arduino D9 подать логическую единицу. Можно осуществлять это с помощью переключателя. Для выбора следующего параметра, увеличения и уменьшения значения параметра, а также сохранения параметра используются кнопки. Данные сохраняются в EEPROM: // Выставляемые настройки // 0 - пауза секунд до выключения // 1 - стоимость 1 мин'О};
502 Часть IV. Интересные проекты на Arduino unsigned int params[]={600,100,100,100,200,200,200,200,200}; unsigned int paramsmin[]={60,50,50,50,50,50,50,50,50}; unsigned int paramsmax[]={1200,1500,1500,1500,1500,1500,1500,1500,1500}; int paramsstep[]={30,5,5,5,5,5,5,5,5}; int tekparam=0; // обработка нажатия кнопок настройки void setOptions(int but) { switch(but) { case 0: // переход/выход в режим программирования // загрузка значений параметров из EEPROM for(int i=0;i<COUNT_BUTTONS;i++) params[i]=get_param_EEPROM(i); view_figurelB); delayB000); view_f igure(params[tekparam],tekparam+12); break; case 1: // следующий параметр tekparam=(tekparam+1)%COUNT_BUTTONS; view_figure(params[tekparam],tekparam+12); break; case 2: // уменьшить значение параметра params [tekparam] =params [tekparam] -paramsstep [tekparam]; params [tekparam] =max (params [tekparam], paramsmin [tekparam]); view_f igure (params [tekparam], tekparam+12) ; break; case 3: // увеличить значение параметра params [tekparam] =params [tekparam] +paramsstep [tekparam]; params [tekparam] =min (params [tekparam], paramsmax [tekparam]); view_f igure (params [tekparam], tekparam+12); break; case 4: // сохранить изменение параметра save_param_EEPRQM (tekparam, params [tekparam]); view_figurelA); delayB000); view_figure(params[tekparam],tekparam+12); break; default: break; Электронный архив Полный вариант рассмотренного скетча находится в папке examples\21\_21_0i сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 22 Arduino и интерфейс USB: управление роботами 22.1. Интерфейс USB Последовательный интерфейс USB используется для подключения периферийных устройств. Соответственно, существуют понятия «главное устройство» — хост (он управляет обменом данными через интерфейс и выступает инициатором обмена) и «периферийное устройство» — клиент (в процессе обмена данными он «подчиняется» хосту). Логика работы хоста и клиента принципиально различна, поэтому нельзя напрямую соединять устройства «хост— хост» и «клиент— клиент». Имеются специальные устройства — хабы, которые подключаются в качестве клиента к одному хосту и в то же время выступают хостом для других периферийных устройств. Хабы также применяются для «разветвления» шины USB. Физически интерфейс USB (до версии 2.0) использует 4 провода (рис. 22.1): ? «земля» (GND); О +5 В (VBUS); ? D-, D+— линии приема/передачи данных (обозначения D+ и D- условны, с электрическими потенциалами это никак не связано). Спецификация USB 1.0 определяла два типа разъемов: А — на стороне контроллера или концентратора USB и В — на стороне периферийного устройства. Впоследствии были разработаны миниатюрные разъемы для применения USB в переносных У5» Standard В Standard А ' #* ii «^, ф Рис. 22.1. Назначение контактов USB 1.0 и USB 2.0
504 Часть IV. Интересные проекты на Arduino и мобильных устройствах, получившие название Mini-USB. Версия миниатюрных разъемов, называемых Micro-USB, была представлена USB Implemented Forum 4 января 2007 г. Примечание Поскольку платы Arduino не несут на борту новомодных USB-разъемов стандартов USB 3 и Туре-С, они здесь и не рассматриваются. Благодаря встроенным линиям питания USB позволяет подключать периферийные устройства без собственного источника питания (максимальная сила тока, потребляемого устройством по линиям питания шины USB, не должна превышать 500 мА, yUSB 3.0 — 900 мА). Стандарт USB поддерживает концепцию plug-and-play. Эта концепция подразумевает, что пользователю достаточно «воткнуть» устройство в соответствующий порт компьютера. Далее его операционная система автоматически определит тип подключенного устройства, найдет подходящий для этого устройства драйвер, сконфигурирует устройство и т. д. Для того чтобы это все работало, стандартом USB предусмотрены некие общие требования для всех устройств: ? каждое устройство содержит дескриптор (описание) устройства; ? есть общий для всех USB-устройств механизм, который позволяет ОС прочитать дескриптор устройства для того, чтобы идентифицировать устройство и узнать его характеристики; ? есть общий для всех USB-устройств механизм, который позволяет ОС выполнить первичную конфигурацию устройства (например, присвоить устройству новый адрес). 22.2. Плата расширения USB Host Shield Очень соблазнительно иметь возможность подключать к Arduino USB-устройства, которых великое множество. Плата расширения USB Host Shield 2.0 (рис. 22.2) позволяет платам Arduino выступать в роли родительского USB-устройства для практически любой имеющейся USB-периферии. С этой платой открывается масса новых возможностей для реализации интересных проектов. В настоящее время платой USB Host Shield 2.0 поддерживаются следующие классы устройств: ? НЮ-устройства — такие как клавиатуры, мыши, джойстики и др.; ? игровые устройства: Sony PlayStation 3, Nintendo Wii, Xbox 360; ? USB-преобразователи: FTDI, PL-2303, ACM, а также некоторые аппараты и GPS-приемники; П Android-устройства; ? цифровые фотоаппараты: Canon (EOS, PowerShot), Nikon и др. Для программирования платы расширения USB Host Shield используется специальная библиотека USB_Host, скачать которую можно со страницы https://github.com/ felis/USB Host Shield 2.0.
Глава 22. Arduino и интерфейс USB: управление роботами 505 Рис. 22.2. Плата расширения USB Host Shield 2.0 Электронный архив Библиотека USBJHost размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Спецификацию и примеры использования библиотеки USBHost можно найти на сайте Circuits@Home по адресу: http://www.circuitsathome.com/arduino_usb_ host_shieldjprojects. 22.3. HID-устройства USB HID (Human Interface Device) — устройство, подключаемое к вычислительной технике с тем, чтобы с ней мог работать человек. Говоря проще, HID — это устройство ввода информации. Устройства ввода необходимы для непосредственного участия человека в работе компьютера: для введения исходных данных для вычислений, для выбора параметров действия, для редактирования имеющихся данных и результатов и т. п. НЮ-устройства ввода различаются по типу вводимой информации: ? для текстовой информации— это преимущественно клавиатуры. Они служат для управления техническими и механическими устройствами (компьютер, калькулятор, кнопочный телефон). Каждой клавише устройства соответствует один или несколько определенных символов. Количество действий, выполняемых с клавиатуры, можно увеличить с помощью сочетаний (комбинаций) клавиш. В клавиатурах такого типа клавиши сопровождаются наклейками с изображением символов или действий, соответствующих нажатию; ? для звуковой информации— это микрофон. Электроакустические приборы, преобразующие звуковые колебания в колебания электрического тока, используются во многих устройствах (телефоны и магнитофоны, приборы звукозаписи и видеозаписи на радио и телевидении, устройства радиосвязи);
506 Часть IV. Интересные проекты на Arduino ? для графической информации: • сканер — устройство для считывания плоского изображения и представления его в растровой электронной форме; • цифровая камера — устройство (фотоаппарат), использующее массив полупроводниковых светочувствительных элементов (матрицу), на которую изображение фокусируется с помощью системы линз объектива. Полученное изображение сохраняется в электронном виде в памяти самой камеры или же на дополнительном цифровом носителе; • веб-камера — цифровая видео- или фотокамера, способная в режиме реального времени фиксировать изображения, предназначенные для дальнейшей передачи по сети Интернет как в потоковом режиме, так и за определенные промежутки времени; • тата захвата (тюнер) — электронное устройство для преобразования аналогового видеосигнала в цифровой видеопоток. Используется для захвата телесигнала, сигнала с камер видеонаблюдения и др. HID-устройства управления различаются по функционалу: ? относительное позиционирование (обрабатывают информацию о перемещении): • мышь — манипулятор, преобразующий механические движения в движение курсора на экране. Различают механические, оптические, гироскопические, сенсорные мыши; • трекбол — манипулятор, чей принцип работы сходен с шариковой механической мышью и аналогичен мыши по принципу действия и по функциям. Однако пользователь не передвигает мышь, а управляет с помощью ладони или пальцев непосредственно шариком, закрепленным на специальном держателе с датчиками; • трекпойнт — миниатюрный тензометрический джойстик, применяемый в ноутбуках для замены мыши. Трекпойнт считывает направление и силу давления пальца пользователя; • тачпад — сенсорная панель, применяемая в основном в портативных компьютерах (ноутбуках). В отличие от трекпойнта, считывающего давление пальца, тачпад считывает емкостные характеристики при соприкосновении пальца с поверхностью емкостных датчиков. Поэтому управление тачпадом с помощью непроводящих предметов (ручка, карандаш, стилус) достаточно проблематично; • джойстик — устройство ввода информации, которое представляет собой качающуюся в двух плоскостях ручку, боковое давление которой считывается датчиками в основании устройства. Используется как игровой гаджет, а также как средство управления (например, роботизированной техникой на производстве); ? абсолютное позиционирование (высчитывают абсолютные координаты на плоскости, в качестве которой выступает устройство):
Глава 22. Arduino и интерфейс USB: управление роботами 507 • графический планшет — устройство для ввода графики (отдельных линий и рисунков) от руки непосредственно в компьютер. Состоит из пера и плоского планшета, чувствительного к нажатию пера; • тачскрин (сенсорный экран)— устройство ввода информации, представляющее собой экран, реагирующий на прикосновения к нему (используется в современных смартфонах, платежных и информационных терминалах). Отдельно хочется выделить специальные НТО-устройства для компьютерных игр: ? игровые мыши — отличаются от обычных компьютерных мышей высокой чувствительностью, настраиваемым весом, большим количеством программируемых кнопок; ? кейпады— специальные игровые клавиатуры-приставки, в которых кнопки скомбинированы для максимального удобства игрока (в современные модели встраивается мини-джойстик); ? руль и педали — манипуляторы для игр жанра «автогонки» (рейсинг); ? джойстики — используются для игр жанра «авиасимуляторы»; ? геймпады— специальные игровые манипуляторы, используемые в аркадных жанрах (перешли с игровых консолей); ? танцевальные платформы— специальные платформы с датчиками давления. Управление производится с помощью ног. Используются для игр жанра «танцевальные аркады»; ? музыкальные инструменты (гитары, барабаны) — специальные манипуляторы в форме музыкальных инструментов с кнопками и датчиками давления. Используются для игр жанра «музыкальные аркады». Операционные системы, как правило, имеют встроенные драйверы НТО-класса, так что у разработчиков отпадает необходимость в трудоемкой разработке собственного драйвера для нового устройства. Чтобы определить устройство как HID, необходимо поддержать ряд структур, описывающих HID-интерфейс, а также написать алгоритм обмена по interrupt-каналу (каналу прерываний) передачи данных. Во многих отношениях устройства НТО не имеют никаких особенных отличий от других USB-устройств. Однако кроме требований, которые относятся ко всем USB- устройствам, устройства НТО выдвигают ряд дополнительных требований: ? НТО-устройство должно иметь interrupt in — конечную точку для выдачи данных в хост, interrupt Out — конечная точка для получения периодических данных от хоста, является опциональной и может не использоваться; ? НТО-уСТрОЙСТВО ДОЛЖНО содержать дескриптор класса Device Class Descriptor И ОДИН ИЛИ более Дескрипторов репорта HID Report Descriptor; ? НТО-устройство должно поддерживать специфический для класса управляющий запрос GetReport, а также опционально поддерживать дополнительный запрос Set_Report;
508 Часть IV. Интересные проекты на Arduino П для передачи interrupt in (данные из устройства в хост) устройство должно положить данные репорта в FIFO соответствующей конечной точки и разрешить передачу; ? для передачи interrupt Out (данные из хоста в устройство) устройство должно разрешить соответствующую конечную точку out, а затем, после прихода пакета, забрать данные из FIFO. 22.4. Подключение HID-мыши USB В библиотеке USBHost 2.0 имеется пример для подключения к Arduino HID-мыши USB — USBHIDBootMouse.pde. Загружаем его и смотрим в мониторе последовательного порта результат работы скетча (рис. 22.3). Рис. 22.3. Скетч для подключения HID-мыши USB 22.5. Управление роботом с помощью руля Defender В этом проекте мы будем управлять роботом с помощью игрового манипулятора — руля Defender Forsage Drift GT (рис. 22.4). Для беспроводной передачи управляющих команд от манипулятора к роботу воспользуемся беспроводным радиомодулем NRF24L01, работу с которым мы рассмотрели в главе 12 (см. разд. 12.3). Руль подсоединяется к плате Arduino через USB Host Shield. Для отправки роботу управляющих команд от руля подсоединяем к этой плате радиомодуль 2>4 ГГц
Глава 22. Arduino и интерфейс USB: управление роботами 509 Рис. 22.4. Руль Defender Forsage Drift GT L293 Рис. 22.5. Электрическая схема блока робота
510 Часть IV. Интересные проекты на Ardumo NRF24L01. Блок робота выполнен в виде гусеничной платформы. К плате Arduino, установленной на роботе, подсоединяется такой же радиомодуль 2,4 ГГц NRF24L01, назначение которого — прием команд, управляющих роботом. Электрическая схема блока робота представлена на рис. 22.5. В качестве драйвера двигателей здесь используется микросхема L293. Загружаем в плату Arduino скетч USBJHIDJDesc.pde (листинг 22.1), входящий в примеры библиотеки USBHost, подсоединяем к Arduino через USB Host Shield наш руль И получаем Descriptor Report Нф-устрОЙСТВа (рис. 22.6). Рис. 22.6. Получение Descriptor Report HID-устройства Usage Page Gen Desktop Ctrls(Ol) Usage Game Pad Collection Application Collection Logical Report Size@8) Report Count@4) Logical Min(OO) Logical Max(FFOO) Physical Min(OO) Physical Max(FFOO) Usage X Usage Y
Глава 22. Arduino и интерфейс USB: управление роботами 511 Usage Z Usage Z Usage Rz Input @0000010) Report Size@4) Report Count@1) Logical Max@7) Physical MaxCB01) UnitA4) Usage Hat Switch Input@1000010) Unit@0) Report Size@1) Report Count @C) Logical Max @1) Physical Max@1) Usage Page Button @9) Usage Min@1) Usage Max @C) Input@0000010) Usage Page Undef@0) Report Size@8) Report CQunt@2) Logical Max@1) Physical Max@1) Usage Input@0000010) End Collection Collection Logical Report Size@8) Report Count@4) Physical Max(FFOO) Logical Max(FFOO) Usage Output@0000010) End Collection End Collection Для создания своего кода возьмем код примера USBHIDJoystick.pde, входящего в библиотеку USBHost и реализующего функционал джойстика, и перепишем файлы hidjoystickrptparser.h (листинг 22.2) и hidjoystickrptparser.cpp (листинг 22.3) для сохранения данных в структуре. Затем добавим отправку данных руля по радиоканалу с помощью библиотеки для радиомодуля NRF24L01 (листинг 22.4). Отправку производим каждые 300 мс.
512 Часть IV. Интересные проекты на Arduino #if !defined ( HIDJOYSTICKRPTPARSER_H_) #define HIDJOYSTICKRPTPARSER_H_ #include <inttypes.h> # include <avr/pgmspace.h> #include "avrpins.h" #include "max3421e.h" #include "usbhost.h" #include lfusb_ch9.h" #include "Usb.h" #if defined(ARDUINO) && ARDUINO >=100 #include "Arduino.h" #else #include <WProgram.h> #endif tinclude "printhex.h" #include "hexdump.h" #include "message.h" #include "confdescparser.h" #include "hid.h" struct GamePadEJventData { uint8_t X, Y, Zl, Z2, Rz; }; class JoystickEvents { public: virtual void OnGamePadChanged(const GamePadEventData *evt) virtual void OnHatSwitch(uint8_t hat); virtual void OnButtonUp(uint8_t but_id); virtual void OnButtonDn(uint8_t but_id); uint8_t X; uint8_t Y; uint8_t Zl; uint8_t Z2; uint8_t Rz; #define RPT_GEMEPAD_LEN 5 class JoystickReportParser : public HIDReportParser { JoystickEvents *joyEvents;
Глава 22. Arduino и интерфейс USB: управление роботами 513_ uint8_t oldPad [RPT_GEMEPAD_LEN] ; uint8_t oldHat; uintl6_t oldButtons; public: JoystickReportParser(JoystickEvents *evt); virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); #endif // HIDJOYSTICKRPTPARSER_H_ Электронный архив Полный вариант рассмотренного скетча (файл hidjoystickrptparser.h) находится в папке examples\22\J22JJ сопровождающего книгу электронного архива (см. приложение 2). #include "hidjoystickrptparser.h" JoystickReportParser::JoystickReportParser(JoystickEvents *evt) : joyEvents(evt), oldHat(OxDE), oldButtons@) { for (uint8_t i=0; i<RPT_GEMEPAD_LEN; i oldPad[i] = OxD; void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { bool match = true; // Checking if there are changes in report since the method was last called for (uint8__t i=0; i<RPT_GEMEPAD_LEN; if (buf[i] != oldPad[i]) { match = false; break; // Calling Game Pad event handler if (!match && joyEvents) { joyEvents->OnGamePadChanged((const GamePadEventData*)buf);
514 Часть IV. Интересные проекты на Arduino for (uint8_t i=0; i<RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i]; } uint8_t hat = (buf[5] & OxF); // Calling Hat Switch event handler if (hat != oldHat && joyEvents) { joyEvents->OnHatSwitch(hat); oldHat = hat; uintl6_t buttons = @x0000 | buf[6]); buttons «= 4; buttons |= (buf[5] » 4); uintl6_t changes = (buttons A oldButtons); // Calling Button Event Handler for every button changed if (changes) for (uint8_t i=0; KOxOC; { uintl6_t mask = @x0001 « i); if (((mask & changes) > 0) && joyEvents) if ((buttons & mask) > 0) j oyEvents->OnButtonDn(i+1); else j oyEvents->OnButtonUp(i+1); } oldButtons = buttons; void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) { X=evt->X; Y=evt->Y; Zl=evt->Z1; Z2=evt->Z2; Rz=evt->Rz; void JoystickEvents::OnHatSwitch(uint8_t hat) { Serial.print("Hat Switch: "); PrintHex<uint8 t>(hat);
Глава 22. Arduino и интерфейс USB: управление роботами Serial.printIn(""); } void JoystickEvents::0nButtonUp(uint8_t but_id) { Serial.print("Up: "); Serial.println(but_id, DEC); } void JoystickEvents::0nButtonDn(uint8_t but_id) { Serial.print("Dn: "); Serial.println(but_id, DEC); Электронный архив Полный вариант рассмотренного скетча (файл hidjoystickrptparser.cpp) находится в папке examples\22\_22JK сопровождающего книгу электронного архива (см. приложение 2). #include <avr/pgmspace.h> #include <avrpins.h> #include <max3421e.h> #include <usbhost.h> #include <usb_ch9.h> #include <Usb.h> #include <usbhub.h> # include <avr/pgmspace.h> #include <address.h> #include <hid.h> #include <hiduniversal.h> #include "hidjoystickrptparser.h" #include <printhex.h> #include <message.h> #include <hexdump.h> #include <parsetools.h> USB Usb; USBHub Hub(&Usb); HIDUniversal Hid(&Usb); JoystickEvents JoyEvents; JoystickReportParser Joy(&JoyEvents) , tinclude <SPI.h> #include <Mirf.h> #include <nRF24L01.h>
516 Часть IV. Интересные проекты на Arduino #include <MirfHardwareSpiDriver.h> #define MAX_BUFF 32 // Буфер приема-передачи void setup() { Serial.begin( 115200 ); Serial.println("Start"); if (Usb.InitO = -1) Serial.println("OSC did not start."); delay( 200 ); if (!Hid.SetReportParser@, &Joy)) ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1 ); Mirf.spi = SMirfHardwareSpi; Mirf.init(); Mirf.setRADDR((byte *)"defender"); // Здесь задаем адрес Mirf.payload = MAX_BUFF; // Здесь задаем буфер Mirf.channel = 10; // Это канал приема-передачи - должен // быть одинаковым у устройств. Mirf .configO ; Serial.println("Start.. "); } char buff[MAX_BUFF]; int c_count = 0; void loopO { Usb.TaskO; buf f [0] =raap (JoyEvents.X, 0,255,1,100) ; buff[1]=map(JoyEvents.Y,0,255,1,100);; buff[2]=map(JoyEvents.Zl,0,255,1,100); buff[3]=map(JoyEvents.Z2,0,255,1,100); buff[4]=JoyEvents.Rz+1; buff[5]=0; Mirf.setTADDR((byte *)"automobilel"); // Адрес! Serial.print(">"); Mirf.send((uint8_t *)buff); while(Mirf.isSending()){ } Serial .println (buff)"; delayC00 ); Электронный архив Полный вариант рассмотренного скетча (файл J22jL.'mo) находится в папке examplesV2\J22JL сопровождающего книгу электронного архива (см. приложение 2).
Глава 22. Arduino и интерфейс USB." управление роботами 517_ Теперь необходимо написать скетч приема данных и отправки команд двигателям робота. Первый байт из буфера данных: влево @-50)— вправо E1-100), второй байт: вперед E1-100)— назад A-50). Надо только правильно перевести байты в данные для микросхемы L293. Полученный скетч представлен в листинге 22.5. tinclude <SPI.h> #include <Mirf.h> #include <nRF24L01.h> #include <MirfHardwareSpiDriver.h> ttdefine MAX_BUFF 32 // Буфер приема-передачи void setup(){ Serial.begin(9600); Mirf.spi = &MirfHardwareSpi; Mirf.initO; Mirf.setRADDR((byte *)"automobilel"); // Здесь задаем адрес Mirf.payload = MAX_BUFF; // Здесь задаем буфер Mirf.channel = 10; // Это канал приема-передачи - должен // быть одинаковым у устройств. Mirf .configO ; // настраиваем выводы для моторов pinModeC, OUTPUT); pinModeD, OUTPUT); pinModeE, OUTPUT); pinModeF, OUTPUT); Serial.println("Start.."); char buff[MAX_BUFF]; void loop(){ delayA0); //receiving if(Mirf.dataReady()) { Mirf.getData((uint8_t *)buff); int fbl=buff[0]; int lr=buff[l]; gol2(fbl,lrl); } delayA00);
518 Часть IV. Интересные проекты на Arduino /I void gol2(int fbfint lr) { // вперед-назад if(fb>50) {digitalWriteC,HIGH);digitalWriteD,LOW); digitalWriteEfHIGH);digitalWriteFrLOW);} else if(fb<50) {digitalWriteC,LOW);digitalWriteD,HIGH); digitalWrite E, LOW) /digitalWrite F,HIGH).;} else {digitalWriteC,LOW)/digitalWriteD,LOW); digitalWriteE,LOW)/digitalWriteF, LOW);} // влево-вправо int lrl=map(absE0-lr),1,50,1,255); int fbl==map(absE0-fb) ,1,50,1,255); if(lr<50) {int left=minB55,max@,fbl-lrl/2)); int right=^ninB55,fbl+lrl/2) } else {int right=minB55,max@,fbl-lrl/2)); int left=minB55,fbl+lrl/2)) analogWrite(9, left); analogWriteA0, right); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\22\j22JM сопровождающего книгу электронного архива (см. приложение 2). 22.6. Управление роботом с помощью геймпада Defender Робот iRobot Create (рис. 22.7) — программируемый робот. Он разработан в 2007 г. компанией iRobot на базе платформы робота-пылесоса Roomba. Электронный интерфейс робота имеет 7 разъемов mini-DIN и разъем DB-25 для обмена данными, электронные датчики и световые индикаторы. Программный интерфейс робота позволяет управлять его поведением: считывать информацию с сенсоров при помощи серии команд, включать команды режима, команды привода, звуковые команды, демонстрационные команды и команды опроса сенсоров. Эти команды можно посылать на последовательный порт робота с компьютера или платы Arduino. Скачать описание открытого интерфейса робота можно по ссылке: http:// www.irobot.com/filelibrary/pdfs/hrd/create/Create%20Open%20Interface v2.pdf.
Глава 22. Arduino и интерфейс USB: управление роботами 519 ?Ш Рис. 22.7. Робот jRobot Create Электронный архив Этот же файл под именем Create Open Interface_y2.pdf содержится в папке datasheets сопровождающего книгу электронного архива (см. приложение 2). Для подсоединения робота к плате Arduino используем его 25-контактный разъем DB-25 с распаянным согласно данным табл. 22.1 переходником (рис. 22.8). Рис. 22.8. Переходник для подключения робота к плате Arduino
520 Часть IV. Интересные проекты на Arduino Таблица 22.1. Соединение платы Arduino и робота iRobot Create iRobot Create 1(Rx) 2(Tx) 14 (GND) 8 (+5 B) Arduino 4 3 GND Vin В этом проекте также задействованы плата расширения USB Host Shield и беспроводной геймпад Defender Scorpion RS3 (рис. 22.9), подключаемый к плате как HID- устройство. Беспроводной геймпад имеет два аналоговых джойстика, 12 кнопок, D- pad и индикатор заряда батарей. На геймпаде может быть включен режим вибрации. Если геймпад не используется в течение продолжительного времени, он автоматически выключается. Совместим этот геймпад с ПК и Sony PlayStation 3. Интерфейс — Bluetooth. Рис. 22.9. Беспроводной геймпад Defender Scorpion RS3 Итак, устанавливаем на плату Arduino шилд USB Host Shield. К USB-входу шилда подсоединяем USB-ресивер геймпада (рис. 22.10) и соединяем плату Arduino с роботом iRobot Create через переходник (см. рис. 22.8) проводами, распаянными согласно табл. 22.1. В библиотеке USBJHost содержится пример для беспроводного джойстика PS3. Чтобы пример заработал с геймпадом Defender Scorpion RS3, необходимо исправить заголовочный файл PS3Enums.h, входящий в эту библиотеку. Исправления касаются значения для кнопок геймпада в массиве buttons [ ].
Глава 22. Arduino и интерфейс USB: управление роботами 521 Рис. 22.10. Подключение USB-ресивера геймпада к USB-входу шилда Электронный архив Исправленный файл PS3Enums.h находится в папке libraries\USB_Host сопровождающего книгу электронного архива (см. приложение 2). Выберем следующее назначение кнопок и джойстиков геймпада: ? правый джойстик — для управления движением; ? кнопка Start/10 — для перевода робота iRobot Create в режим fiill (полное управление); ? кнопка Select/9 — для перевода iRobot Create в пассивный режим; ? кнопки R1 и R2 — для проигрывания мелодий iRobot Create; ? кнопки LI, L2 — для установки светодиодов iRobot Create; О кнопка 4 (квадрат) — выполнение скрипта движения по квадрату. Скетч для Arduino, представленный в листинге 22.6, отслеживает состояние кнопок и джойстиков и выполняет отправку команд на iRobot Create. iinclude <SoftwareSerial.h> SoftwareSerial mySerialD,3); #include <PS3USB.h> USB Usb; PS3USB PS3(&Usb); Button Btarr[16]={UP, RIGHT, DOWN, LEFT, SELECT, START, L3, R3, L2, R2, LI, Rl, TRIANGLE, CIRCLE, CROSS, SQUARE}; AnalogHat Joyarr[4]={LeftHatX,LeftHatY,RightHatX,RightHatY};
522 Часть IV. Интересные проекты на Arduino boolean printAngle; uint8_t state = 0; int status_buttons[16] = {0,0,0,0,0,0,0, 0,0,0,0, 0,0,0,0,0}; long millisjDuttons[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; long millis_buttonsl[16]={500, 500, 500, 500; 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000}; int status_joys[4]={0,0,0,0}; int millis_joys[4]={0,0,0,0}; int map_joys[4]={2000,-500,2000,-500}; int dHatLeft=0; int dHatRight=0; void setup() { Serial.beginA15200) ; if (Usb.InitO — -1) { Serial.print(F("\r\n OSC did not start")); whileA); //halt } Serial.print (F(lf\r\nPS3 USB Library Started")); my'Serial. begin E7600) ; } void loopO { Usb.TaskO; // joys for(int i=0;i<4;i++) { if((PS3.getAnalogHat(Joyarr[i]) != status_joys[i]) && (millis()-millis_joys[i]>200)) { millis_joys[i]=millis();status_joys[i]=PS3.getAnalogHat(Joyarr[i]); int hat=map(PS3.getAnalogHat(Joyarr[i]),0,255,map_joys[i]* (-l),map_joys[i]); if(i<2) dHatLeft=l; else dHatRight=l; if(dHatLeft>0) {dHatLeft=0;} if(dHatRight>0) {dHatRight=0; go_irobot(status_joys[3],status_joys[2]);}
Глава 22. Arduino и интерфейс USB: управление роботами 523 II buttons for(int i=0;i if(PS3.getButtonPress(Btarr[i]) && status_buttons[i]==0) millis_buttons[i]=millis();status_buttons[i]=l; put_actions(i); } else {if(millis()-millis_buttons[i]>millis_buttonsl[i]) status_buttons[i]=0;} // действия по нажатии кнопки void put_actions(int btnl) { switch(btnl) { case 4: mySerial.writeA28); break; // start case 5: mySerial.writeA32); break; // led case 8: mySerial.write((intI39); mySerial.write((int)8); mySerial.write((intJ55)/mySerial.write((intJ55); break; case 10: mySerial.writeA39);mySerial.writeD); mySerial.write((byteH);mySerial.writeB55); break; // music 6 case 9: mySerial.writeA41);mySerial.writeF); break; // 40 см и останов case 12: mySerial.writeA52);mySerial.writeA3);mySerial.writeA37) ; mySerial.writeA);mySerial.writeD4);mySerial.writeA28); mySerial.write((byteH);mySerial.write((intI56); mySerial.write((intI) ;mySerial.write((intI44); mySerial.writeA37);mySerial.write((byteH);mySerial.write((byte)O); mySerial.write((byteH);mySerial.write((byte)O);mySerial.writeA53); break; // music 5 case 11: mySerial.writeA41);mySerial.writeE); break; case 13: mySerial.writeA52);mySerial.writeA7);mySerial.writeA37) ; mySerial.writeA);mySerial.writeD4);mySerial.writeA28); mySerial.write((byteH);mySerial.write((intI56); mySerial.write((intI);mySerial.write((intI44);
524 Часть IV. Интересные проекты на Arduino mySerial.writeA37) ;mySerial.writeA) ;mySerial.writeD4); mySerial.write((byte)O);mySerial.write(l);mySerial.write((intI57); mySerial.write((byteH);mySerial.write(90); mySerial.writeA53);mySerial.writeA53); break; default: break; // отправка команд для движения робота void go_irobot(int vl,int rl) { int v2,r2,lfl,rtl; if(vl<128 && rl<128) {lfl=vl+abs(rl~128)/2;rtl=max@fvl-abs(rl-128)/2);} else if(vl<128 && rl>=128) {lfl=max@,vl-abs(rl-128)/2);rtl=vl+abs(rl-128)/2;} else if(vl>=128 && rl<128) {Ifl=vl-abs(rl-128)/2;rtl=rainB55,vl+abs(rl-128)/2);} else if(vl>=128 && rl>=128) {Ifl=minB55,vl+abs(rl-128)/2);rtl=vl-abs(rl-128)/2;} else lfl=raap(lfl,0,255,250,-250); rtl^map (rtl,0,255,250,-250); mySerial.writeA45); mySerial.write(highByte(rtl));mySerial.write(lowByte(rtl)); mySerial.write(highByte(If1));mySerial.write(lowByte(If1)); Электронный архив Полный вариант рассмотренного скетча находится в папке examples\22\_22J>6 сопровождающего книгу электронного архива (см. приложение 2). Для загрузки скетча в плату Arduino предварительно скопируйте его на своем компьютере в папку libraries\USB_Host\examples, откройте в Arduino IDE и нажмите на кнопку Загрузить.
ГЛАВА 23 Камера Pixy: реализация компьютерного зрения Камера Pixy (рис. 23.1) — это популярная система машинного зрения для Arduino и Raspberry Pi. В отличие от большинства камер, Pixy самостоятельно выполняет обработку изображения, освобождая мощности микроконтроллера для других задач. Встроенные в ее прошивку алгоритмы способны обнаруживать и отслеживать множество объектов одновременно. Рис. 23.1. Камера PixyCam В частности, для обнаружения объектов Pixy использует алгоритм цветовой фильтрации на основе оттенков. Поэтому объект должен иметь четкий цветовой оттенок. Камера может одновременно распознавать объекты семи цветовых оттенков, которые можно запрограммировать. Обмен данными с микроконтроллерами осуществляется по одному из интерфейсов: I2C, SPI, UART или через аналоговые выходы. Pixy может находить сотни объектов за кадр. Она использует алгоритм связанных компонентов, чтобы определить, где один объект начинается, а другой заканчива-
526 Часть IV. Интересные проекты на Arduino ется. Затем Pixy компилирует размеры и местоположение каждого объекта и передает их через один из своих интерфейсов (например, SPI). 23.1. Настройка камеры В качестве утилиты настройки для Pixy используется PixyMon — она работает как в ОС Windows, так и в Mac OS и в Linux. Версию для Windows можно скачать по ссылке: http://cmucam.Org/attachments/download/1246/pixymonwindows-2.0.9.exe. Скачав и установив утилиту PixyMon, подсоединим камеру Pixy к компьютеру с помощью USB-кабеля и запустим утилиту. Для настройки камеры на определенные цвета выбираем в меню пункт Action | Set Signature<X>... и на изображении выделяем с помощью мыши участок нужного цвета. Так же поступаем и для настройки камеры на следующие шесть сигнатур. После этого на экране видим выделение объектов для всех настроенных сигнатур (рис. 23.2). Рис. 23.2. Определение объектов запрограммированных сигнатур в утилите PixyMon
Глава 23. Камера Pixy: реализация компьютерного зрения 527 Рис. 23.3. Тонкая настройка сигнатур в программе PixyMon Для каждой сигнатуры в меню File | Configure можно установить и более тонкие настройки (рис. 23.3). 23.2. Подключение камеры Pixy к плате Arduino Подключение камеры Pixy к плате Arduino выполняется с помощью входящего в комплект камеры переходника, который подсоединяется к контактам ISCP платы Arduino (рис. 23.4). Общение камеры с платой Arduino при этом осуществляется по интерфейсу SPI. Для программирования камеры используется библиотека Pixy. Установим библиог теку в Arduino IDE и запустим из этой библиотеки пример hello_world. Как можно видеть, в монитор последовательного порта с камеры Pixy выводятся данные об обнаруженных объектах (рис. 23.5). Электронный архив Библиотека Pixy размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2).
528 Часть IV. Интересные проекты на Arduino Рис. 23.4. Подключение камеры Pixy к плате Arduino Рис. 23.5. Вывод в монитор последовательного порта данных, приходящих в Arduino с камеры Pixy
Глава 23. Камера Pixy: реализация компьютерного зрения 529_ 23.3. Организация слежения камерой за объектом В этом проекте мы организуем слежение камерой Pixy, укрепленной на подвесе из двух сервоприводов, за объектом определенного цвета. Система ищет объект с цветом заданной сигнатуры и с максимальными размерами, определяет удаление его центра от центра камеры и дает команды на сервомоторы для смещения подвеса. Монтажная схема этого проекта представлена на рис. 23.6, а содержимое скетча, обеспечивающего ее работу, — в листинге 23.1. Загружаем скетч в плату Arduino и проверяем работу системы. fntzmg Рис. 23.6. Монтажная схема подключения к плате Arduino камеры Pixy, укрепленной на подвесе из двух сервоприводов // подключение библиотек #include <SPI.h> #include <Pixy.h> #include <Servo.h> // создание объектов Pixy pixy; Servo servoX; Servo servoY;
530 Часть IV. Интересные проекты на Arduino void setup() { Serial.begin(9600); Serial.print("Starting..An") servoX.attach(9); servoY.attachA0); // запуск pixy pixy.initO ; void loop О 0; static int i int j; uintl6_t blocks; char buf[32]; // получить данные с камеры blocks = pixy.getBlocks(); // если блоки найдены if (blocks) // анализируем 1 .из каждых 50 кадров if (i%50—0) sprintf(buf, "Detected %d:\n", blocks); Serial.print(buf); for (j=0; j<blocks; j++) sprintf(buf, " block %d: ", j); // если объект сигнатуры 1 int maxlength=0; int maxlengthx=0; int maxlengthy=0; int centerx=0; int centery=0; if(pixy.blocks[j].signature==l) { Serial.print(pixy.blocksfj].signature);Serial.print(" "); Serial.print(pixy.blocks[j].x);Serial.print(" "); Serial.print(pixy.blocks[j].y);Serial.print(" "); Serial.print(pixy.blocks[j].width);Serial.print(" "); Serial.print(pixy.blocks[j}.height);Serial.printIn(" "); // вычисляем максимальный объект if(maxlength<pixy.blocks[j].width || maxlength<pixy.blocks[j].height) { maxlength=max(pixy.blocks[j].width,pixy.blocks[j].height);
Глава 23. Камера Pixy: реализация компьютерного зрения 531 maxlengthx=pixy.blocks[j].width; maxlengthy^pixy.blocks[j].height; centerx=pixy.blocks[j].x+pixy.blocks[j].width/2; centery=pixy.blocks[j],y+pixy.blocks[j].height/2; } // поворот сервоприводов servoX.write(map(centerx,0+50,320-50,0,180)); servoY.write(map(centery,0+50,240-50,0,180)); Электронный архив Полный вариант рассмотренного скетча находится в папке examples№3\_23JI сопровождающего книгу электронного архива (см. приложение 2).
ГЛАВА 24 Проекты на плате Nano 33 BLE Sense Arduino Nano 33 BLE Sense (рис. 24.1) — компактная платформа для разработки на чипе u-blox NJNA-B306, который включает в себя 32гбитный микроконтроллер Nordic nRF52840 на архитектуре ARM Cortex-M4 с тактовой частотой 64 МГц, 1 МБайт флеш-памяти и 256 КБайт оперативной памяти. Чип NINA-B306 обеспечивает связь Bluetooth v5.0 в диапазоне 2,4 ГГц и поддерживает энергосберегающий протокол Bluetooth Low Energy (BLE). На плате расположено несколько датчиков: ? 9-осевой IMU-сенсор (инерциальное измерительное устройство) на чипе LSM9DS1, включающий в себя акселерометр, компас и магнитометр; ? датчик температуры и относительной влажность воздуха HTS221; ? датчик атмосферного давления LPS22HB; ХП датчик Avago APDS-9960, который позволяет распознавать базовые жесты (взмах руки влево или вправо, вверх-вниз и вперед-назад), а также цвета через интенсивность каналов RGB и уровень освещенности; ? встроенный цифровой микрофон MP34DT05, который можно использовать для распознавания коротких голосовых команд или записи звука. Назначение контактов платы Arduino Nano 33 BLE Sense показано на рис. 24.2. Рис. 24.1. Плата Arduino Nano 33 BLE Sense
Глава 24. Проекты на плате Nano 33 BLE Sense 533 ARDUINO SYMBOL Рис. 24.2. Назначение контактов платы Arduino Nano 33 BLE Sense 24.1. Начало работы с платой Nano 33 BLE Sense Чтобы осуществлять программирование платы Nano 33 BLE Sense в среде Arduino IDE, необходимо установить для нее поддержку. Для этого командой меню Инструменты | Плата | Менеджер плат заходим в Менеджер плат, находим плату типа nano зз ые, нажимаем кнопку Установка (рис. 24.3) и подтверждаем установку программного обеспечения в открывшемся окне Безопасность Windows (рис. 24.4). После установки программного обеспечения и драйверов (рис. 24.5) в списке плат среды Arduino IDE появится плата Arduino Nano 33 BLE и обнаружится подключение к порту (рис. 24.6). Теперь на плату необходимо загрузить любой скетч — например, Blink, и проверить ее работоспособность. Кроме того, для работы с датчиками, установленными на плате, необходимо установить через Менеджер библиотек (Скетч | Подключить библиотеку | Управлять библиотеками) следующие библиотеки: ? HTS221 — для датчика температуры и влажности; О LPS22HB — для барометрического датчика;
534 Часть IV. Интересные проекты на Arduino О APDS9960 — для датчика жестов, освещенности, цвета и приближения; ? LSM9DS1 —для DVfU-датчика (акселерометр, гироскоп, магнитометр); ? PDM — для микрофона; ? ArduinoBLE — для передачи данных по Bluetooth BLE. Рис. 24.3. Поиск платы типа nano 33 Ые Рис. 24.4. Установка поддержки для Arduino Nano 33 BLE Sense в Arduino IDE
Глава 24. Проекты на плате Nano 33 BLE Sense 535 Рис. 24.5. Завершение установки ПО и драйверов Рис. 24.6. Плата Arduino Nano 33 BLE готова к работе
536 Часть IV. Интересные проекты на Arduino Электронный архив Библиотеки HTS221, LPS22HB, APDS9960, LSM9DS1, RDM и ArduinoBLE размещены в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). Загрузите примеры из этих библиотек на плату и проверьте их работу. 24.2. Bluetooth Low Energy (BLE) Bluetooth Low Energy (BLE) является частью спецификации Bluetooth 4.0, которая также включает протокол классического Bluetooth и протокол высокоскоростного Bluetooth (Classic Bluetooth and Bluetooth High Speed Protocols). По сравнению с классическим Bluetooth BLE требует меньшей мощности при сохранении аналогичного диапазона связи. Дело, в том, что BLE — это технология, которая всегда отключена и передает только короткие объемы данных, когда это необходимо. Это значительно снижает энергопотребление, что делает технологию BLE идеальной для использования в случаях, когда требуется постоянное долговременное соединение с низкой скоростью передачи данных. В BLE есть два основных понятия: ? GAP, Generic Access Profile — общий профиль доступа; ? GATT, Generic Attribute Protocol — протокол общих атрибутов. Общий профиль доступа (GAP) ответствен за подключение и распространение информации о наличии устройства BLE. Он отвечает за видимость устройства во внешнем мире, а также играет важную роль в определении того, как устройство взаимодействует с другими устройствами. На основе BLE функционируют два вида устройств; периферийные и центральные. Процесс обеспечения видимости устройств (advertising process) заключается в том, что периферийное устройство каждые 2 с отправляет в окружающую среду данные о своем существовании. Если эти данные получит центральное устройство, оно отправит запрос на сканирование. В ответ периферийное устройство пришлет данные результата сканирования. Используя общий протокол данных (протокол атрибутов GATT), два устройства BLE обмениваются данными друг с другом на основе таких понятий, как сервис (service) и характеристика (characteristic). Протокол GrATT сохраняет все сервисы и характеристики в справочной таблице с использованием 16-битных идентификаторов* Сервис может иметь много характеристик. Уникальность каждого сервиса обеспечивается универсальным уникальным идентификатором (UUID), который может иметь размер 16 битов— для официальных адаптированных сервисов или 128 битов — для пользовательских сервисов. Характеристики содержат одну точку данных и схожи с сервисами, при этом каждая характеристика имеет свой уникальный идентификатор (UUID), который отличает ее от другой характеристики.
Глава 24. Проекты на плате Nano 33 BLE Sense 537 Согласно спецификации Special Interest Group (SIG) для устройств BLE1 любое устройство BLE, которое официально приняло UUID от SIG, должно использовать идентификатор, указанный им в своих приложениях. 24.3. Отправка данных с датчиков платы Nano 33 BLE Sense no BLE Рассмотрим отправку данных с датчиков HTS221 (относительной влажности воздуха и температуры) и LPS22HB (барометрический датчик) платы Nano 33 BLE Sense по BLE. Используем для этого библиотеку ArduinoBLE (листинг 24.1). Сначала определяем BLE сервиса (uuid — 0x1815 Automation IO): BLEService meteoBLEsense(815"); и характеристики для температуры, влажности и атмосферного давления: BLEIntCharacteristic meteoTemperatureChar(A6E", BLERead | BLENotify); BLEUnsignedlntCharacteristic meteoHumidityChar(A6F", BLERead | BLENotify); BLEUnsignedlntCharacteristic meteoPressureChar(AA3", BLERead I BLENotify); Данные UUID для сервиса и характеристик берем в спецификации SIG для характеристик и сервисов. После чего отправляем данные замеров с периодичностью 1 раз в 3 с. // подключение библиотек #include <ArduinoBLE.h> Hnclude <Arduino_HTS221.h> ¦include <Arduino_LPS22HB.h> // определение BLE сервиса (uuid - 0x1815 Automation 10 ) BLEService meteoBLEsense(815"); // характеристики сервиса BLEIntCharacteristic meteoTemperatureChar(A6E", BLERead I BLENotify); BLEUnsignedlntCharacteristic meteoHumidityChar(A6F", BLERead | BLENotify); BLEUnsignedlntCharacteristic meteoPressureChar(AA3", BLERead | BLENotify); // служебные переменные float temperature; float humidity; float pressure; unsigned long millissend; void setup() { // запуск последовательного порта Serial.begin(9600); 1 Для характеристик см.: https://www.bluetooth.com/speciflcations/gatt/characteristics/, для сервисов — https://www.bluetooth.com/speciflcations/gatt/services/.
538 Часть IV. Интересные проекты на Arduino while (!Serial); // запуск датчика влажности и температуры if (!HTS.begin()) { Serial.println("Failed to initialize humidity temperature sensor!"); while A); } // запуск барометра if (!BARO.begin()) { Serial.println("Failed to initialize pressure sensor!"); while A); pinMode(LED_BUILTIN, OUTPUT); // запуск BLE if (!BLE.begin()) { Serial.println("BLE failed to Initiate"); delayE00); while A); // чтение данных с датчиков temperature = HTS.readTemperature(); humidity = HTS.readHumidity(); pressure = BARO.readPressureO; BLE.setLocalName("ArduinoMeteoBLEsense"); BLE.setAdvertisedService (meteoBLEsense); meteoBLEsense.addCharacteristic(meteoTemperatureChar); meteoBLEsense.addCharacteristic(meteoHumidityChar); meteoBLEsense.addCharacteristic(meteoPressureChar); BLE.addService(meteoBLEsense); meteoTemperatureChar.writeValue(temperature*100); meteoHumidityChar.writeValue(humidity*100); meteoPressureChar.writeValue(pressure*7.5); // advertising process BLE.advertise(); Serial.println("Bluetooth device is now active, waiting for connections..."); void loopO { - // подключение центрального устройства BLEDevice central = BLE.central(); if. (central) { Serial.print("Connected to central: "); Serial.println(central.address()); digitalWrite(LED_BUILTIN, HIGH);
Глава 24. Проекты на плате Nano 33 BLE Sense 539 while (central.connected()) { if (millis()-millissend>=3000) { temperature = HTS.readTemperature(); humidity = HTS.readHumidity(); pressure = BARO.readPressureO; meteoTemperatureChar.writeValue(temperature*100); meteoHumidityChar.writeValue(humidity*100); meteoPressureChar.writeValue(pressure*7.5); Serial.println("Meteo BLEsense data:"); Serial.print("t=")/Serial.print(temperature);Serial.print(" °C"); Serial.print (" h=") /Serial.print (humidity);Serial.print (" %") ; Serial.print(" p=")/Serial.print(pressure);Serial.print(" kPa"); Serial.println(" "); Serial.println(""); // millissend=mLllis(); digitalWrite(LED_BUILTIN, LOW); Serial.print("Disconnected from central: "); Serial.println(central.address()); В качестве центрального устройства мы используем смартфон на ОС Android с установленным приложением nrfConnect (рис. 24.7). i% Mi Smart Band 4 Й8? Е8-07:2F:ID-.2/4:^0 ЫЯ BONDED 0x1300 Рис. 24.7. (Часть 1 из 2) Получение данных из периферийного устройства (nrfConnect)
540 Часть IV. Интересные проекты на Arduino Рис. 24.7. (Часть 2 из 2) Электронный архив Полный вариант рассмотренного скетча находится в папке *xampte&\24\j>4j)i сопровождающего книгу электронного архива (см. приложение 2). 24.4. EM6nHOTeicaTensorFlow Lite TensorFlow — открытая программная библиотека для машинного обучения, разработанная компанией Google для решения задач построения и тренировки нейронной сети с целью автоматического нахождения и классификации образов, достигая качества человеческого восприятия. Ее вариант TensorFlow Lite — это облегченное решение TensorFlow для мобильных и встроенных устройств. Специалисты компании Arduino совместно с командой TensorFlow Lite разработали библиотеку TensorFlow Lite Micro для Arduino Nano 33 BLE Sense. Эта библиотека доступна в Arduino Library Manager (Скетч | Подключить библиотеку | Управлять библиотеками). Установите ее, как показано на рис. 24.8.
Глава 24. Проекты на плате Nano 33 BLE Sense 541 Рис. 24.8. Установка библиотеки Tensorflow Lite Электронный архив Библиотека TensorFlow Lite Micro размещена в каталоге libraries сопровождающего книгу электронного архива (см. приложение 2). В библиотеке TensorFlow Lite Micro содержатся примеры на предварительно обученных моделях: ? micro_speech — распознавание речи с помощью встроенного микрофона; О magicwand — распознавание жестов с использованием встроенного IMU; О persondetection — обнаружение человека с помощью внешней камеры ArduCam. Загрузите й плату и проверьте в действии пример micro_speech. При произнесении слова «yes» светодиод горит пару секунд зеленым цветом, при произнесении слова «по» светодиод горит пару секунд красным цветом. Предварительно обученная нейронная сеть работает на плате Nano 33 BLE Sense. 24.5. Пример создания классификатора объектов с обучением В этом проекте мы создадим классификатор объектов с использованием нейронной сети, запущенной на плате Arduino Nano 33 BLE Sense. Для этого воспользуемся библиотекой TensorFlow Lite Micro и датчиком цвета APDS9960, расположенным на плате. Классификацию объектов мы проведем по цвету — например, определим фрукты разного цвета: зеленое яблоко, банан, апельсин, огурец, помидор. Проект состоит из следующих этапов: сбор данных, обучение и развертывание классификаторов.
542 Часть IV. Интересные проекты на Arduino 24.5.1. Сбор данных Соберем данные для обучения нашей модели в TensorFlow, предлагая устройству наши фрукты и овощи в качестве объектов разного цвета. Загрузите в плату Arduino Nano 33 BLE Sense скетч из листинга 24.2, с помощью которого будут получены цветовые данные объектов. // подключение библиотек #include <Arduino_APDS9960.h> void setup() { // запуск последовательного порта Serial.begin(9600); while (!Serial) {}; // инициализация датчика APDS9960 if (!APDS.begin()) { Serial.println("Error initializing APDS9960 sensor."); } // заголовок Serial.println("Red,Green,Blue"); void loopO { int r, g, b, c, p; float sum; // ожидание данных датчика while (!APDS.colorAvailable() I I !APDS.proximityAvailable()) {} // чтение данных с датчика APDS.readColor(r, g, b, с); sum = r + g + b; p = APDS.readProximity() ; // если объект находится близко и достаточно хорошо освещен if (р == 0 && с > 10 && sum > 0) { float redRatio = г / sum; float greenRatio = g / sum; float blueRatio = b / sum; // вывод в CSV-формате Serial.print(redRatio/ 3); Serial.print(\ '); Serial.print(greenRatio, 3);
Глава 24. Проекты на плате Nano 33 BLE Sense 543 Serial.print(',f); • Serial.print(blueRatio, 3); Serial.println(); Здесь для каждого объекта, который мы хотим классифицировать, собираются некоторые данные о его цвете. Перемещайте плату с датчиком вдоль и вокруг поверхности каждого объекта, чтобы захватить все возможные вариации его цвета. Датчик цвета отправляет данные о цвете объекта в формате журнала CSV в последовательный порт (рис. 24.9). Выводимые в монитор данные (включая первую строку: Red, Green, Blue) сохраняем в файлы *.csv. Выполнив описанную операцию для всех объектов, получим в результате соответствующее количество файлов: banana.csv, greenapple.csv, orange.csv, tomato.csv, cucumber.csv. Электронный архив Полный вариант рассмотренного скетча находится в папке examples\24\_24JJ сопровождающего книгу электронного архива (см. приложение 2). Рис. 24.9. Отправка данных цвета объекта в монитор последовательного порта
544 Часть IV. Интересные проекты на Arduino 24.5.2. Обучение модели Для обучения нейронной сети воспользуемся Google Colab — бесплатным облачным сервисом на основе Jupyter Notebook. Google Colab предоставляет все необходимое для машинного обучения прямо в браузере и дает бесплатный доступ к невероятно быстрым процессорам нейронных сетей GPU и TPU. С помощью Google Colab вы можете легко обучить свою модель за считанные секунды. В Colab предустановлена библиотека Tensorflow и практически все необходимые для работы python-библиотеки. Если какой-то пакет отсутствует, он с легкостью устанавливается на ходу через pip или apt-get. Чтобы начать работать с Colab, сначала необходимо войти в свою учетную запись Google, а затем перейти по этой ссылке: https://colab.research.google.com (рис. 24.10). Рис. 24.10. Вход в Google Colab: создание нового ноутбука Затем устанавливаем необходимые пакеты, набрав в Поле выполнения кода соответствующие команды (рис. 24.11). После чего можно загрузить необходимые данные — CSV-файлы, полученные в разд. 24.5.1 (рис. 24.12). Теперь можно приступать к обучению нейронной сети. Выполнение кода, осуществляющего анализ CSV-файлов и преобразование их в формат, который будет использоваться для обучения подключенной нейронной сети, показано на рис. 24.13. Результат такого анализа и преобразования показан на рис. 24.14.
Глава 24. Проекты на плате Nano 33 BLE Sense 545 Рис. 24.11. Установка необходимых пакетов Рис. 24.12. Загрузка файлов с данными
546 Часть IV. Интересные проекты на Arduino Рис. 24.13. Анализ CSV-файлов и преобразование их в формат, который будет использоваться для обучения подключенной нейронной сети Рис. 24.14. Преобразование файлов с данными к необходимому формату
Глава 24. Проекты на плате Nano 33 BLE Sense 547 Рис. 24.15. Создание и обучение модели TensorFlow с помощью высокоуровневого API Keras Далее создаем и обучаем модель TensorFlow с помощью высокоуровневого API Keras (рис. 24.15). Поместим наши тестовые данные в модель (рис. 24.16) и подготовим прогнозы (рис. 24.17). Преобразуем модель в формат TFlite (рис. 21.18). Создадим постоянный байтовый массив, который содержит модель TFlite, и импортируем его как файл model.h (рис. 24.19). Рис. 24.16. Помещение тестовых данных в модель
548 Часть IV. Интересные проекты на Arduino Рис. 24.17. Прогнозы при загрузке тестовых данных в модель Рис. 24.18. Преобразование модели в формат TFIite Рис. 24.19. Создание файла model.h
Глава 24. Проекты на плате Nano 33 BLE Sense 549 Осталось щелкнуть на файле model.h двойным щелчком для его скачивания на компьютер. Электронный архив Файл RrojectsArduino_glava24.ipynb для среды Google Colab находится в папке examples\24 сопровождающего книгу электронного архива (см. приложение 2). 24.5.3. Скетч классификатора для запуска нейронной сети на плате Nano 33 BLE Sense Создадим скетч классификатора (листинг 24.3), при этом скопировав в папку скетча файл model.h, который содержит модель TFlite (Tensorflow lite) (рис. 24.20). iorfK:>rt si Рис. 24.20. Добавление в скетч файла model.h // Подключение библиотек // Arduino_TensorFlowLite - Version: 0.alpha.precompiled #include <TensorFlowLite.h>
550 Часть IV. Интересные проекты на Arduino #include <tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h> #include <tensorflow/lite/experimental/micro/itiicro_error_reporter.h> #include <tensorflow/lite/experimental/micro/micro_interpreter.h> #include <tensorflow/lite/schema/schema_generated.h> #include <tensorflow/lite/version.h> #include <Arduino_APDS9960.h> #include "model.h" // глобальные переменные, используемые в TensorFlow Lite (Micro) tflite::MicroErrorReporter tflErrorReporter; tflite::ops::micro::A110psResolver tflOpsResolver; const tflite::Model* tflModel = nullptr; tflite::MicroInterpreter* tfllnterpreter = nullptr; TfLiteTensor* tfllnputTensor = nullptr; TfLiteTensor* tflOutputTensor = nullptr; // Буфер памяти для tensorflow lite constexpr int tensorArenaSize = 8 * 1024; byte tensorArena[tensorArenaSize]; // массив для сопоставления индекса объектов с именем const char* CLASSES[] = { "Banana", "Cucumber", "GreenApple", "Orange", "Tomato" #define NUM_CLASSES (sizeof(CLASSES) / sizeof(CLASSES[0])) void setup() { Serial.begin(9600); while (!Serial) {}; Serial.println("Object classification using RGB color sensor"); Serial, print In (" "); Serial.println("Arduino Nano 33 BLE Sense running TensorFlow Lite Micro") Serial.printIn(ии); if (!APDS.begin()) { Serial.println("Error initializing APDS9960 sensor."); // получите представление TFL массива байтов модели tflModel = tflite::GetModel(model);
Глава 24. Проекты на плате Nano 33 BLE Sense 551_ if (tflModel->version() != TFLITE_SCHEMA_VERSION) { Serial.printIn("Model schema mismatch!"); while A); // Создание интерпретатора для запуска модели tfllnterpreter = new tflite::Microlnterpreter(tflModel, tflOpsResolver, tensorArena, tensorArenaSize, &tflErrorReporter) // Выделите память для входных и выходных тензоров модели tflInterpreter->AllocateTensors(); // Получить указатели для входных и выходных тензоров модели tfllnputTensor = tfllnterpreter->input@); tflOutputTensor = tfllnterpreter->output@); void loop() { int r, g, b, p, c; float sum; // пока есть данные с датчика while (!APDS.colorAvailableО I I !APDS.proximityAvailable()) {} // чтение данных APDS.readColor(r, g, b, c); p = APDS.readProximity(); sum = r + g + b; // если объект находится близко и достаточно хорошо освещен if (р == 0 && с > 10 && sum > 0) { // normalize float redRatio = г / sum; float greenRatio = g / sum; float blueRatio = b / sum; // входящие данные для tensorflow tflInputTensor->data.f[0] = redRatio; tflInputTensor->data.f[1] = greenRatio; tflInputTensor->data.f[2] = blueRatio; // запуск сети TfLiteStatus invokeStatus = tfllnterpreter->lnvoke(); if (invokeStatus != kTfLiteOk) { Serial.println("Invoke failed!"); while A); return;
552 Часть IV. Интересные проекты на Arduino // вывести результаты for (int i - 0;' i < NUM_CLASSES; i++) { Serial.print(CLASSES[i]); Serial.print(" ")/ Serial.print(int(tflOutputTensor->data.f[i] * 100)); Serial.print("%\n")/ } Serial.println(); // ожидание данных датчика while (!APDS.proximityAvailable() I I (APDS * readProximity() — 0)) {} Загружаем скетч в плату и проверяем работу классификатора объектов (рис. 24.21). Электронный архив Полный вариант рассмотренного скетча находится в папке ехатр1е$\24\т24„03 сопровождающего книгу электронного архива (см. приложение 2). .. ®э«в^Шв;1 Рис. 24.21. Работа классификатора объектов
ПРИЛОЖЕНИЯ Приложение 1. Перечень использованных источников Приложение 2. Описание электронного архива
ПРИЛОЖЕНИЕ 1 Перечень использованных источников ? http://www.arduino.cc — официальная документация проекта Arduino; ? http://www.cxem.net — авторские материалы с сайта «Паяльник»; ? http://www.playarduino.ru — авторские материалы с сайта PlayArduino; ? Петин В. А. Лекции по Arduino для школ Казахстана (будут распространяться только на DVD в школах Казахстана).
ПРИЛОЖЕНИЕ 2 Описание электронного архива Электронный архив с материалами, сопровождающими книгу, можно скачать с FTP-сервера издательства «БХВ-Петербург» по ссылке ftp://ftp.bhv.ru/ 9785977567114.zip, а также со страницы книги на сайте www.bhv.ru. В архиве находятся следующие папки: ? \examples — исходники примеров и проектов глав 7-24 для Arduino IDE; ? Mibrariee — библиотеки Arduino, используемые в примерах и проектах книги и не включенные в среду разработки Arduino IDE; ? \datasheets— документация производителей (data sheet) на рассматриваемые в книге микросхемы и модули.
Предметный указатель Android-приложение IoT MQTT Dashboard 307 API Яндекс.Карт 472 Applnvertor2 447 Arduino Due 27,28 Arduino GPRS/GSM Shield 464 Arduino Leonardo 21,26-28,149,367 Arduino LilyPad 24,25 Arduino Mega 145,149 Arduino Mega2560 25,26,149 Arduino MKR 21 Arduino MKR WiFi 29,308,309 Arduino Nano 22,23 Arduino Nano 33 21,30,31 Arduino Nano Every 30,31 Arduino Pro Mini 23,24 Arduino Uno 21,22 ArduinoYun21,28 В Bluetooth-модуль HC-05 271,273,277,440, 442,452 Cosmo WiFi Connect Shield 32 EasyVR Arduino Shield 32 Ethernet Shield 31 GPS-приемник V.KEL VK16E 461 GSM/GPRS Shield 31 H LEGO Mindstorms 372 LilyPad Arduino 328,364 M МАС-адрес устройства 279,281,282,284,285, 287,458 MicroSD Shield 31 Motor Shield 31 MP3 Shield 31 Music Shield 32 N Nano 33 BLE Sense 30,31 О OLED-дисплеи 192 Raspberry Pi 3$6-390 RGB-светодиод 115,342-344 Robot Operating System (ROS) 376 Video Overlay Shield 32 w Wi-Fi модуль ESP8266 398-406 Wi-Fi/Bluetooth модуль ESP32 399 X XBee Shield 31 НТО-интерфейс 507 НТО-устройства 504-507,510 ZIP-архив библиотеки 133
558 Предметный указатель Адаптер USB-Serial 202, 334 Амплитудная модуляция 266 Аналоговые 0 входы Arduino 45,107 0 датчики 153 ° температуры LM335 154,283,284,291,292, 295,296 ¦ температуры ТМР36 421,423,424,428,429, 435,437,440,442,444,452,455 Аналого-цифровой преобразователь (АЦП 46 Аппаратные UART-контроллеры 140 Аппаратура радиоуправления 264 Арифметические операторы 54 Базовая структура программы для Arduino 48 Бесколлекторные двигатели 245,246 Беспроводная передача данных 248 Беспроводной геймпад Defender Scorpion RS3 520 Беспроводной интерфейс Bluetooth 271 Беспроводной радиомодуль NRF24L01 257-259, 508,510,511 Библиотека 0 Adafruit SSD1306 192 0 Adafruit Thermal Printer 404 0 Adafruit Unified Sensor 185 0 Adafruit_GFX_Library 187,190 0 BH1750FVI170,172 0 BMP280 183 0 DHT165,423 0 Esplora 342, 343,344 0 Ethernet 279 0 IRRemote249 0 Keyboard 341 0 LiquidCrystal 175,176 0 LiquidCrystalJ2C 181 0 Mouse 341 0 NRF24L01 511 0 OLEDJ2C192 0 OneWirel56 0 Pixy 527 0 RCSwitch254 0 RF24 260 0 rosjib 378,379 0 rosserial378 0 sdfatlib280 0 Serial 140 0 Servo 235,246 0 SoftwareSerial 149,272 0 USB_Host 504,505, 508,510,511,520 0 Wire 168 0 ZUNO_DHT414 Библиотеки Arduino 37,38,132 В Веб-камера 369 Вентилятор 432 Графический дисплей Nokia5110 186 Датчик 153,154,157 0 AvagoAPDS-9960 532 0 атмосферного давления LPS22HB 532 0 атмосферного давления и температуры ВМР280 183 0 влажности и температуры DHT11 259,373,374, 414,421,423,424,428,429,435,436,440,442, 443,452,454 0 движения HC-SR501 310-312,370,371,535,544 0 расстояния НС SR-04 237 0 температуры и относительной влажности воздуха HTS221 532 0 точного времени DS3231 193,194 Дисплей 173,175,197 0 Nextion 197,203,206 0 WH0802 385,386 Драйверы двигателей 231 0 L293 510,517 0 L298 231,233 0 шагового двигателя А4988 240 Дребезг 101,102 Дробление шага шагового двигателя 242 ж Жидкокристаллические дисплеи 173 ЖК-дисплей Nokia5110 426,427,431 ЖК-индикаторы 192 Заголовочный файл библиотеки 136 и Игра «Змейка» 344 Издатель (publisher) 378,379,383,384 ИК-приемник 248,249,251,252 ИК-приемопередатчик 248 ИК-пульт248,251 Интернет вещей 279,* 367,402,451 Интерфейс 0 I2C192 0 SPI527 0 USB503 Искусственные источники света 432 Исполнительное устройство 225
Предметный указатель 559 К Камера Pixy 472, 525,527-529 Клиент 503 Конвертор I2C 181 Контроллер PCD8544 186 Купюроприемник 476-480,492 л Логические операторы 55 Люминесцентные лампы 433 м Мембранный вакуумный насос 432 Менеджер библиотек 132,133 Микрокомпьютер Atheros AR9331 28, 367 Микроконтроллер 0 ATmega45 ¦ ATmegal280 20 ¦ ATmegal68 20,22-25 ¦ ATmegal68V 362 ¦ ATmega2560 20,25 о ATmega328 20-25,334,362 ¦ ATmega328V 362,365 ¦ ATmega32U4 20,26,28,334,335,341,362,367 0 ATSAMD21G18 29 0 ESP32 399 0 HD44780 173,175 0 Microchip ATmega328P 31 0 Microchip ATmega4809 18 Микропроцессор Atmel SAM3X8E ARM Cortex-M3 20 Микросхема 0 DS3231 193,405,406 0 ESP8266 398 0 L293 510,517 0 MCP23017 128 0 SIM900 289 0 WiznetW5100 279 Модуль 0 влажности почвы 422 0 часов реального времени DS3231 405 Монетоприемник 476,483,484,492 Монитор последовательного порта 39 Мультиплексор CD4051 129,130 н Назначение статического IP-адреса 280,302 Настройка контрастности 174,175 Нормально разомкнутая кнопка 97 Облачная среда разработки Arduino Create 35 Операторы сравнения 49,55 Основные АТ-команды 273 Отладочная 0 информация Arduino 142,149 0 плата NodeMCU 403,407,409 п Передатчик 0 FS1000A254 0 НК-Т6А265,268,269 Переменные 62,82 Плата Arduino 181,188,190,192,205,207 0 Esplora 340-344,346, 350,355 0 Leonardo 334,335,337,338,340,368 0 LilyPad362 0 Mega 389-391 0 Nano 33 BLE Sense 532-534,540-542,550 0 Yun367 Плата 0 GSM GPRS SIM900 Shield 464 0 LilyPad Arduino USB 363 0 LilyPad Simple Snap 364 0 Z-Uno 409-414 Плата расширения 0 Arduino Yun shield 367-369 0 Ethernet shield 279-284 0 Ethernet shield W5100 451 0 GSM/GPRS Shield 289,292,295 0 Relay shield 431,433 0 USB Host Shield 504,505, 508,510,520 Платы расширения (шилды) 31 Подключение 0 библиотек 135 0 русского шрифта 193 Подписчик (subscriber) 379,383 Подтягивающий резистор 97-100 Поиск устройств, подключенных к шине I2C 168 Получение IP-адреса по DHCP 280,281,302 Последовательный интерфейс USB 503 Последовательный порт 0 UART 140,141,150 0 USB334 Потенциометр 109,115,175 Преобразователь USB-Serial 365 Приемник 0 НК-Т6А267,268 0 MX-RM-5V256,257 Приложение Bluetooth Terminal 446,447 Программа Nextion Editor 197 Программатор 334 Протокол 0 1-Wire 156,157,379 0 Bluetooth 440 0 Bluetooth Low Energy (BLE) 532,536 0 I2C 167-169,172,181,183,373 0 MQTT 300,304,305,307 0 MQTT (Message Queue Telemetry Transport) 299 0 радиопередачи данных Z-Wave 409-411,415
560 Предметный указатель Процессор О Atmel SAM3X8E ARM Cortex-M3 27 О SitaraAM1808(ARM9K72 Радиоканал 433,920 МГц 254 Радиомодуль 0 передатчика FS1000A 254 0 приемника MX-RM-5V 254 Разменный автомат (хоппер) 476,485,486 Реле 225,226 Робот iRobot Create 518-520 Руль Defender F.orsage Drift GT 508,509 Сайт «Народный мониторинг» 282-284, 287-289, 295,299,300,301, 304-306,451 Светодиодная матрица 207 Светодиодные фитолампы 433 Светодиодный индикатор 118 0 D5651 118,136 Сдвиговый регистр 74НС595 122-124 Сенсор 153 Сервис Arduino IoT Cloud 312,313,316 Сервопривод 234,238,251 0 MG995 235 Символьные дисплеи 173,186 Система GPS 461 Скетч 37 Создание библиотек 136 Среда 0 Arduino Create 41^43 0 разработки Arduino IDE 35,37 Стягивающий резистор 97,99,100 Схема с общим эмиттером 226 Файл 0 keywords.txt 135,138 0 реализации библиотеки 137 Фоторезистор 337,421,423,424,428,429,435,437, 440,442,444,452,455 Фреймворк WeblOPi 387 Функции 48, 53,64, 78, 79 0 клавиатуры 335 0 мыши 337 х Хост 503, 507, 508 ц Цифровой датчик 0 влажности и температуры DHT 163 0 интенсивности света ВН1750 169 0 температуры DS18B20 157 Цифровой микрофон MP34DT05 532 Цифровые 0 входы Arduino 97 0 выводы Arduino 87 0 порты ввода/вывода 45 Частотная модуляция 266 ш Шаговые двигатели 239,240 0 Nema 17 240 Широтно-импульсная модуляция (ШИМ) 46,114 Твердотельные реле 227,229 Термопринтер 402,403 Типы данных 56 Узлы ROS 381 Унарные операторы 56 Управляющие операторы 49 Установка драйверов Arduino 35 Утилита PixyMon 526, 527 Электродвигатели постоянного тока 229 Электромагнитное реле 225,226 Электронные часы 193,195 Эмулятор компьютерной мыши 338, 339 Энергонезависимая память EEPROM 145-147,148 Энергосберегающие лампы дневного спектра 433 Язык Processing/Wiring 35