Text
                    
Чарльз Белл MicroPython для микроконтроллеров и проектов IoT
Charles Bell MicroPython for the Internet of Things A Beginner’s Guide to Programming with Python on Microcontrollers Second Edition
Чарльз Белл MicroPython для микроконтроллеров и проектов IoT Руководство для начинающих по программированию микроконтроллеров на языке Python 2025
Белл Ч. MicroPython для микроконтроллеров и проектов IoT - 2024. - 498 с. Книга поможет вам быстро и легко научиться программировать микрокон-троллеры и устройства IoT (интернета вещей). Автор опирается на практический подход, не углубляясь в теорию. Вы изучите ряд проектов, в которых используется язык MicroPython – разновидность одного из наиболее широко используемых язы-ков сценариев, и на основе этого материала сможете разрабатывать собственные IoT-приложения. Книга адресована тем, кто малознаком с электроникой и миром интернета вещей и хочет освоить простой способ работы с этим оборудованием без необ-ходимости долго изучать программирование на языках C++ или C.
Я посвящаю эту книгу своей старшей сестре, пробудившей во мне жажду знаний, научив читать словарь в раннем возрасте
Содержание Об авторе...................................................................................................................15 О рецензенте. ...........................................................................................................16 Благодарности..........................................................................................................17 Введение....................................................................................................................18 Глава 1. Что такое интернет вещей?.................................................................23 Интернет вещей и вы.................................................................................................24 Интернет вещей – это больше, чем просто подключение к интернету.........25 IoT-сервисы. ............................................................................................................27 Краткий обзор IoT-решений.................................................................................28 Управление транспортным парком.....................................................................35 Интернет вещей и безопасность..........................................................................37 Python и интернет вещей. .........................................................................................41 История Python.......................................................................................................41 Онлайн-симулятор MicroPython..........................................................................42 Итоги.............................................................................................................................46 Глава 2. Знакомство с MicroPython...................................................................47 Начало работы.............................................................................................................47 История MicroPython. ............................................................................................49 Возможности MicroPython. ...................................................................................50 Ограничения MicroPython.....................................................................................51 На чем может быть запущен MicroPython?. .......................................................52 Экспериментируем с Python на ПК......................................................................52 Как работает MicroPython..........................................................................................58 Консоль REPL...........................................................................................................59 Подключение платы...............................................................................................59 Запуск консоли REPL (Windows)...........................................................................60 Запуск консоли REPL (macOS и Linux).................................................................63 Запуск MicroPython.....................................................................................................63 Дополнительное оборудование............................................................................65 Пример 1. Мигание светодиода............................................................................69 Пример 2. Последовательное мигание светодиодов.........................................70 Пример 3. Использование кнопки.......................................................................72 Итоги.............................................................................................................................74
Содержание  7 Глава 3. Аппаратные средства MicroPython...................................................75 Начало работы с платами MicroPython. ..................................................................75 Обновления прошивки..........................................................................................76 Проблемы с сетью...................................................................................................77 Один шаг за раз!. ....................................................................................................77 Программные инструменты.................................................................................78 Требуется доработка. .............................................................................................80 Контакты GPIO........................................................................................................81 Другие советы. ........................................................................................................81 Платы MicroPython RP2040........................................................................................86 Происхождение RP2040.........................................................................................87 Raspberry Pi Pico/Pico W.........................................................................................89 Arduino Nano RP2040 Connect...............................................................................98 Другие платы RP2040...........................................................................................102 Платы, совместимые с MicroPython.......................................................................104 BBC micro:bit. ........................................................................................................104 Circuit Playground Express....................................................................................107 Выносные модули и дополнительные компоненты............................................111 Выносные модули.................................................................................................111 Специализированные платы расширения.......................................................114 Аксессуары для конкретных плат контроллеров.............................................115 Макетная плата для Raspberry Pi Pico/Pico W...................................................115 Аксессуары для BBC micro:bit.............................................................................116 Какую плату приобрести?........................................................................................117 Итоги...........................................................................................................................120 Глава 4. Как программировать на MicroPython..........................................121 Базовые концепции..................................................................................................122 Блоки кода.............................................................................................................123 Комментарии. .......................................................................................................124 Арифметические и другие операторы..............................................................124 Вывод на экран. ....................................................................................................125 Переменные. .........................................................................................................127 Типы. ......................................................................................................................128 Базовые структуры данных.....................................................................................129 Списки....................................................................................................................129 Кортежи..................................................................................................................130 Словари..................................................................................................................131 Операторы. ................................................................................................................132 Условные операторы............................................................................................132 Циклы.....................................................................................................................133 Модульность: модули, функции и классы.............................................................135 Включаемые модули............................................................................................135 Функции.................................................................................................................135 Классы и объекты. ................................................................................................137 Изучение Python на примерах................................................................................144
8  Содержание Пример 1. Использование циклов. ....................................................................144 Пример 2. Использование сложных данных и файлов...................................147 Пример 3. Использование функций. .................................................................152 Пример 4. Использование классов.....................................................................159 Дополнительная информация................................................................................166 Итоги...........................................................................................................................167 Глава 5. Библиотеки MicroPython....................................................................168 Встроенные и стандартные библиотеки...............................................................169 Обзор......................................................................................................................170 Общие стандартные библиотеки. ......................................................................172 Встроенные функции и классы. .........................................................................183 Исключения...........................................................................................................187 Библиотеки MicroPython..........................................................................................190 Обзор......................................................................................................................190 Общие библиотеки MicroPython. .......................................................................191 Пользовательские библиотеки. ..............................................................................194 Итоги...........................................................................................................................197 Глава 6. Низкоуровневая поддержка оборудования...............................199 Библиотеки, специфичные для RP2040.................................................................200 Класс Flash. ............................................................................................................200 Классы PIO и StateMachine..................................................................................201 Библиотеки для конкретных плат..........................................................................201 Библиотеки, специфичные для Raspberry Pi Pico............................................202 Библиотеки, специфичные для Arduino Nano RP2040 Connect. ....................202 Работа с оборудованием на низком уровне..........................................................203 Драйверы и библиотеки спешат на помощь!...................................................204 Сетевой протокол времени (NTP)......................................................................205 Обратные вызовы.................................................................................................208 Использование внешних модулей. ........................................................................210 Протокол I2C. ........................................................................................................211 Последовательный периферийный интерфейс SPI. .......................................218 Итоги...........................................................................................................................223 Глава 7. Электроника для начинающих.........................................................225 Основы........................................................................................................................226 Инструменты.........................................................................................................226 Мультиметр...........................................................................................................227 Паяльник................................................................................................................228 Инструменты для зачистки проводов...............................................................230 Штатив для плат «третья рука»...........................................................................231 Использование мультиметра..............................................................................232 Питание электроники..........................................................................................239 Электронные компоненты. .....................................................................................241
Содержание  9 Кнопки....................................................................................................................241 Конденсаторы. ......................................................................................................242 Диоды.....................................................................................................................243 Предохранители. ..................................................................................................244 Светодиоды (LED).................................................................................................245 Реле.........................................................................................................................247 Резисторы..............................................................................................................248 Переключатели. ....................................................................................................249 Транзисторы..........................................................................................................250 Стабилизаторы напряжения...............................................................................251 Внешние модули и схемы....................................................................................252 Использование макетной платы для создания схем...........................................253 Что такое датчики?...................................................................................................256 Как датчики измеряют. .......................................................................................257 Примеры датчиков...................................................................................................260 Акселерометры.....................................................................................................261 Аудиодатчики........................................................................................................261 Считыватели штрих-кодов..................................................................................261 Биометрические датчики....................................................................................262 Емкостные датчики..............................................................................................262 Датчики монет (монетоприемники).................................................................263 Датчики тока.........................................................................................................263 Датчики изгиба/силы (тензорезистивные)......................................................264 Датчики газа..........................................................................................................264 Датчики света........................................................................................................264 Датчики расхода жидкости.................................................................................266 Датчики уровня жидкости. .................................................................................266 Датчики местоположения...................................................................................267 Считыватели магнитных полос..........................................................................267 Магнитометры......................................................................................................268 Датчики влагосодержания..................................................................................268 Датчики приближения.........................................................................................269 Датчики радиации................................................................................................270 RFID-датчики. .......................................................................................................271 Датчики скорости.................................................................................................271 Переключатели и кнопки....................................................................................272 Датчики наклона. .................................................................................................272 Сенсорные датчики..............................................................................................272 Видеодатчики........................................................................................................272 Датчики погоды (метеодатчики). ......................................................................273 Итоги...........................................................................................................................274 Глава 8. Проект «Hello, World!» в стиле MicroPython. ..............................276 Начало работы с проектами MicroPython.............................................................276 Один шаг за раз!. ..................................................................................................277 Требуется доработка. ...........................................................................................277
10  Содержание Обращаться осторожно!. .....................................................................................278 Обзор. .........................................................................................................................278 Необходимые компоненты. ....................................................................................280 Настройка оборудования.........................................................................................282 Соединения для Raspberry Pi Pico......................................................................282 Соединения для Arduino Nano RP2040 Connect. ..............................................284 Написание кода.........................................................................................................287 Проектирование. ..................................................................................................288 Требуемые библиотеки........................................................................................288 Планирование кода..............................................................................................290 Тестирование внешних модулей........................................................................297 Выполнение кода......................................................................................................301 А что дальше?............................................................................................................303 Итоги...........................................................................................................................305 Глава 9. Проект: пешеходный переход.........................................................306 Обзор. .........................................................................................................................307 Необходимые компоненты. ....................................................................................307 Настройка оборудования.........................................................................................308 Соединения для Raspberry Pi Pico......................................................................309 Соединения для Arduino Nano RP2040 Connect. ..............................................310 Написание кода.........................................................................................................312 Импорт...................................................................................................................312 Настройки..............................................................................................................312 Функции.................................................................................................................314 Выполнение кода......................................................................................................318 А что дальше?............................................................................................................319 Итоги...........................................................................................................................319 Глава 10. Проект: мониторинг растений.......................................................321 Обзор. .........................................................................................................................321 Необходимые компоненты. ....................................................................................323 Настройка оборудования.........................................................................................324 Соединения для Raspberry Pi Pico......................................................................325 Соединения для Arduino Nano RP2040 Connect. ..............................................325 Написание кода.........................................................................................................327 Калибровка датчика.............................................................................................327 Часть 1. Модуль кода датчика.............................................................................330 Часть 2. Код таймера чтения...............................................................................337 Часть 3. Основной код..........................................................................................338 Выполнение кода......................................................................................................347 А что дальше?............................................................................................................349 Итоги...........................................................................................................................350
Содержание  11 Глава 11. Проект: мониторинг погоды . ........................................................351 Обзор. .........................................................................................................................351 Необходимые компоненты. ....................................................................................352 Настройка оборудования.........................................................................................353 Соединения для Raspberry Pi Pico......................................................................353 Соединения для Arduino Nano RP2040 Connect. ..............................................354 Написание кода.........................................................................................................356 Тестирование модуля BME280............................................................................356 Часть 1. Модуль чтения датчика.........................................................................359 Часть 2. Чтение кода таймера.............................................................................363 Часть 3. Основной код..........................................................................................363 Выполнение кода......................................................................................................370 А что дальше?............................................................................................................372 Итоги...........................................................................................................................373 Глава 12. Облачные вычисления.....................................................................374 Обзор. .........................................................................................................................375 Что такое облако?. ................................................................................................375 Что такое облачные вычисления?......................................................................375 Как облако помогает интернету вещей?...........................................................376 Облачные системы интернета вещей (IoT Cloud)................................................377 Доступные облачные IoT-сервисы.....................................................................377 Что могут сделать облачные сервисы для наших проектов интернета вещей?....................................................................................................................380 Начало работы с облачными вычислениями. ......................................................380 Протокол сообщений телеметрии (MQTT). ......................................................381 Arduino IoT Cloud..................................................................................................382 Сервис ThingSpeak................................................................................................383 Итоги...........................................................................................................................384 Глава 13. Arduino IoT Cloud................................................................................385 Обзор. .........................................................................................................................385 Начало работы с Arduino IoT Cloud........................................................................386 Создание учетной записи Arduino.....................................................................387 Создать новое устройство...................................................................................389 Использование Arduino IoT Cloud с MicroPython. ...........................................392 Установка библиотек Arduino IoT Cloud............................................................398 Проект: пешеходный переход IoT..........................................................................404 Необходимые компоненты.................................................................................404 Настройка оборудования. ...................................................................................405 Настройка Arduino IoT Cloud..............................................................................406 Написание кода.....................................................................................................409 Выполнение кода..................................................................................................416 А что дальше?........................................................................................................416
12  Содержание Итоги...........................................................................................................................417 Глава 14. MQTT с Adafruit IO..............................................................................418 Обзор. .........................................................................................................................419 Клиенты. ................................................................................................................421 Брокеры..................................................................................................................421 Начало работы с Adafruit IO....................................................................................422 Создать учетную запись на Adafruit IO.............................................................422 Проект: погода IoT....................................................................................................433 Необходимые компоненты.................................................................................433 Настройка оборудования. ...................................................................................433 Написание кода.....................................................................................................435 Выполнение кода......................................................................................................442 А что дальше?............................................................................................................445 Итоги...........................................................................................................................446 Глава 15. Сервис ThingSpeak.............................................................................447 Обзор. .........................................................................................................................447 Начало работы с ThingSpeak. ..................................................................................448 Создание учетной записи в ThingSpeak............................................................448 Создание канала...................................................................................................449 Получение ключа API...........................................................................................451 Использование ThingSpeak с MicroPython........................................................452 Проект: IoT-мониторинг растений........................................................................459 Создание канала...................................................................................................459 Настройка ThingSpeak..........................................................................................460 Необходимые компоненты.................................................................................466 Настройка оборудования. ...................................................................................467 Написание кода.....................................................................................................469 Выполнение кода......................................................................................................473 А что дальше?............................................................................................................475 Итоги...........................................................................................................................475 Глава 16. Куда двигаться дальше?..................................................................477 Новые проекты для изучения.................................................................................477 Примеры проектов MicroPython........................................................................478 Форумы. .................................................................................................................478 Документация.......................................................................................................478 Репозитории..........................................................................................................479 Сайты проектов сообщества: Hackster.io..........................................................480 Хранилище знаний learn.adafruit.com. .............................................................482 Присоединяйтесь к сообществу..............................................................................484 Зачем вносить вклад?. .........................................................................................484 Какая лицензия, где ее узнать?. .........................................................................485 Как делиться..........................................................................................................486
Содержание  13 Представляйте свой уникальный дизайн.........................................................486 Проверка лицензии..............................................................................................488 Делайте уместные вещи......................................................................................489 Аннотируйте свою работу...................................................................................490 Будьте хорошим гражданином...........................................................................491 Рекомендуемые сообщества...............................................................................492 Стать мейкером.........................................................................................................493 Что такое мейкер?. ...............................................................................................493 Делитесь своими идеями. ...................................................................................494 Посещайте мероприятия.....................................................................................494 Итоги...........................................................................................................................495 Предметный указатель. ......................................................................................496

Об авторе Чарльз Белл – исследователь в области новых технологий. Является членом группы разработки Oracle MySQL для облачной инфраструктуры Oracle в качестве главного разработчика решений высокой доступности и резервного копирования службы MySQL HeatWave в Oracle Call Interface (OCI). Проживает в маленьком городке сельской Вирджинии со своей любящей женой. Доктор Белл получил докторскую степень в области инженерных наук в Virginia Commonwealth University в 2005 году. Его исследовательские интересы включают системы баз данных, разработку программного обеспечения, сенсорные сети и 3D-печать. Свое ограниченное свободное время он проводит в качестве практикующего мастера, уделяя особое внимание микроконтроллерам, 3D-принтерам и проектам печати.
О рецензенте Сай Яманур – инженер по встраиваемым системам, проживающий в Окленде, Калифорния. Имеет более чем десятилетний опыт работы в качестве эксперта по встраиваемым системам, проектированию и внедрению аппаратного и программного обеспечения. Является соавтором трех книг по использованию Raspberry Pi для реализации проектов «Сделай сам» (DIY), а также разработчиком панели мониторинга личного здоровья, представленной на ярмарках Maker Faires по всей стране. Сай работает над проектами по улучшению качества жизни (QoL) людей с хроническими заболеваниями. Ознакомьтесь с его проектами на https://saiyamanoor.com1. 1 См. также книгу Сай Яманур, Шрихари Яманур «Raspberry Pi Pico в любительских проектах»: ДМК Пресс, 2022. – Прим. перев.
Благодарности Я хотел бы поблагодарить всех специалистов компании Apress, талантливых и энергичных. Я ценю понимание и терпение моего редактора Марка Пауэрса и выпускающего редактора Нирмала Сельвараджа. Они сыграли важную роль в успехе этого проекта. Я также хотел бы поблагодарить армию профессионалов издательского дела в Apress за то, что они помогли книге так хорошо выглядеть в печати, и выражаю особую благодарность рецензентам за их мудрые советы и мягкие подталкивания в правильном направлении. Всем большое спасибо! Самое главное, я хочу поблагодарить мою жену Аннет за ее бесконечное терпение и понимание, пока я проводил так много времени со своим ноутбуком.
Введение Решения интернета вещей (IoT) не так сложны, как может показаться из названия. Действительно, интернет вещей – это, по сути, другое название того, чем мы уже занимаемся. Возможно, вы слышали о «подключенных устройствах», «интернет-готовности» или даже о «поддержке облачных технологий». Все это относится к одному и тому же – будь то одно устройство, такое как тостер или монитор состояния растений, или сложный продукт с несколькими устройствами, такой как решения для домашней автоматизации. Все они имеют одну общую черту: к ним можно получить доступ через интернет для отображения данных или управления. Хитрость заключается в применении знаний о технологиях, чтобы извлечь из них максимальную выгоду для вашего решения интернета вещей. В этой книге мы исследуем, как создавать IoT-решения на небольших специализированных платах микроконтроллеров с помощью простого для понимания языка программирования MicroPython. Для кого написана эта книга? Я написал эту книгу, чтобы поделиться своей страстью к IoT-решениям на основе Python. Особенно мне хотелось показать, как каждый может программировать свои собственные решения интернета вещей с использованием MicroPython на небольших платах микроконтроллеров, таких как Raspberry Pi Pico и Arduino Nano RP2040 Connect. Таким образом, целевая аудитория включает всех, кто интересуется созданием решений интернета вещей, любителей и энтузиастов, которые не хотят тратить много времени на изучение сложного языка программирования для управления оборудованием. Как устроена эта книга Книга была написана, чтобы помочь читателю перейти от общих знаний о микроконтроллерах и MicroPython к опыту разработки IoT-решений. Первые несколько глав охватывают общие темы, включая краткое введение в интернет вещей, доступные платы микроконтроллеров, а также в то, как работает MicroPython. В последующих главах представлено руководство по программированию на MicroPython, а также введение в электронику. Далее следуют четыре проекта, которые вы можете реализовать, чтобы потренироваться в создании базовых решений IoT на MicroPython. Потом мы на-
Введение  19 учимся использовать облачные системы, чтобы сделать наши IoT-решения полезными. На протяжении всей книги приведены примеры того, как реализовать многие из представленных концепций. Ниже приводится краткий обзор каждой главы, включенной в эту книгу.   Глава 1 «Что такое интернет вещей?». В этой главе представлены ответы на вопросы о том, что такое интернет вещей и как создаются IoT-решения. Вы познакомитесь с некоторой терминологией, описывающей архитектуру интернета вещей, а также с некоторыми примерами известных IoT-решений. Глава завершается демонстрацией MicroPython.   Глава 2 «Знакомство с MicroPython». В этой главе представлен обзор того, что такое MicroPython и как начать его использовать.   Глава 3 «Аппаратные средства MicroPython». В этой главе обсуждаются некоторые примеры аппаратного обеспечения, доступного для MicroPython, включая Raspberry Pi Pico, Arduino Nano RP2040 Connect и несколько других альтернативных плат. В главе также представлены некоторые аксессуары для каждой платы.   Глава 4 «Как программировать на MicroPython». В этой главе представлено руководство по обучению программированию на MicroPython. Оно охватывает основы языка, необходимые для начала написания собственных сценариев MicroPython.   Глава 5 «Библиотеки MicroPython». В этой главе представлен обзор различных библиотек MicroPython, доступных для использования в ваших сценариях. Глава включает множество примеров того, как начать использовать библиотеки для взаимодействия с оборудованием.   Глава 6 «Низкоуровневая аппаратная поддержка». В этой главе представлен обзор низкоуровневых аппаратных абстракций, доступных для портов плат Pyboard и Wi-Fi MicroPython. Особенности библиотек представлены вместе с несколькими полными примерами, демонстрирующими функциональность.   Глава 7 «Электроника для начинающих». В этой главе представлено краткое введение в электронику, включая типы компонентов, которые вы будете использовать в книге, а также список рекомендуемых инструментов. Глава завершается обзором типов датчиков, доступных для решений интернета вещей.   Глава 8 «Проект: “Hello, World!” в стиле MicroPython»: в этой главе представлен практический проект, который поможет вам начать программировать аппаратное обеспечение и создавать решения MicroPython. Проект представляет собой часы, запрограммированные на MicroPython с использованием модуля часов реального времени (RTC).   Глава 9 «Проект: пешеходный переход». В этой главе представлен еще один практический проект, который взаимодействует со светодиодами и кнопками для создания имитации пешеходного светофора. Проект также демонстрирует, как удаленно управлять оборудованием через веб-страницу.
20  Введение   Глава 10 «Проект: мониторинг растений». В этой главе представлен более сложный практический проект, который демонстрирует, как получать данные с датчиков и просматривать их через интернет. Проект представляет собой решение для мониторинга состояния растений, которое вы можете расширить от одного до нескольких образцов.   Глава 11 «Проект: мониторинг погоды». В этой главе представлен последний практический проект, объединяющий все, что мы узнали из книги, для создания работающего IoT-решения. Проект представляет собой небольшой модуль метеодатчиков (датчиков погоды), который использует новые облачные сервисы Adafruit IO для хранения и визуализации данных.   Глава 12 «Облачные вычисления». В этой главе представлены облачные вычисления, включая краткое описание основных поставщиков облачных вычислений и их услуг, а также какие услуги вы можете рассмотреть для использования в своих проектах интернета вещей.   Глава 13 «Облако Arduino IoT». В этой главе представлено руководство по созданию проектов с использованием двух основных сервисов облачных вычислений; один можно использовать бесплатно, а другой предоставляется по платной подписке. Вы увидите, как начать работу с каждой из этих услуг.   Глава 14 «MQTT с Adafruit IO». В этой главе используется проект главы 10 для демонстрации того, как превратить его в облачный интернет-сервис для демонстрации результатов любому пользователю в интернете.   Глава 15 «Сервис ThingSpeak». Эта глава расширяет проект главы 10 и показывает, как можно использовать облачные вычисления для создания визуальных эффектов и подключения результатов к другим службам облачных вычислений для дополнительного анализа.   Глава 16 «Куда двигаться дальше?». Эта глава завершает обзор IoTрешений MicroPython предложениями по изучению большего количества проектов и источников, где можно найти идеи для новых проектов, получить ответы на вопросы или решения проблем, с которыми вы можете столкнуться при разработке собственных IoT-проектов. В главе также обсуждается, как вы можете присоединиться к сообществу энтузиастов интернета вещей, MicroPython и электроники, став мейкером 1. Как пользоваться этой книгой Цель этой книги – помочь вам узнать больше о том, что такое интернет вещей, открыть для себя возможности MicroPython и узнать, как создавать собственные IoT-решения. 1 Мейкер (Maker) – букв. «тот, кто делает», созидатель. – Прим. перев.
Введение  21 Если у вас уже есть собственная плата Raspberry Pi Pico или Arduino Nano RP2040 Connect и вы знакомы с некоторыми темами, изложенными в начале книги, я рекомендую вам просмотреть соответствующие главы и познакомиться с представленным контекстом, чтобы было легче понять и самостоятельно реализовывать примеры, приведенные в последующих главах. Вы также можете прочитать некоторые главы не по порядку, чтобы продолжить работу над конкретным проектом, но я рекомендую впоследствии вернуться к пропущенному материалу и убедиться, что вы получили все представленные сведения. Если вы только начинаете работать с MicroPython и микроконтроллерами, я рекомендую прочитать книгу полностью, прежде чем разрабатывать собственные IoT-решения. Многие примеры, представленные в первых главах, являются исходным материалом для того, что будет изложено в последующих разделах. Текстовые соглашения В этой книге используется ряд текстовых соглашений. Код в тексте: указывает служебные слова, имена переменных в тексте, имена функций, названия программных библиотек. Пример: «Это установит соединение для запуска функции flash_led() при нажатии кнопки Button1». Блок кода задается следующим образом: sudo apt-get update sudo apt-get install python3.11 Ввод или вывод из командной строки записывается так: >>> print("Hello World") Имена файлов, имена папок, расширения файлов, пути файловой системы. Пример: «Файл rp2040-datasheet.pdf содержит документацию микроконтроллера RP2040». Также выделяются курсивом названия разделов на страницах сайтов, например «Вы можете найти ссылку на информационной странице книги на вкладке Source Code/Downloads». Названия в меню или диалоговых окнах выделены жирным шрифтом с русским переводом в скобках. Например, «Чтобы создать новое устройство, перейдите на вкладку Devices (Устройства) на панели инструментов и нажмите Add (Добавить)». Следующим образом предоставлены объемные врезки: Врезка Выделяется следующим образом.
22  Введение СОВЕТЫ или ПРИМЕЧАНИЯ выделяются вот так. Загрузка кода Код примеров, показанных в этой книге, доступен на веб-сайте Apress www. apress.com. Вы можете найти ссылку на информационной странице книги на вкладке Source Code/Downloads. Эта вкладка расположена в разделе Related Titles на странице книги. Как связаться с автором Если у вас возникнут какие-либо вопросы или комментарии – или вы даже заметите ошибку, о которой, по вашему мнению, мне следует знать, – вы можете связаться со мной по адресу drcharlesbell@gmail.com.
Глава 1 Что такое интернет вещей? Если вы в последнее время следите за миром технологий, скорее всего, вы встречали многочисленные упоминания термина «интернет вещей» (Internet of Things, IoT). Большинство упоминаний в средствах массовой информации и рекламных объявлениях компаний называют то или иное «интернетом вещей», но почти или совсем не объясняют, что это значит. Даже если вы обнаружите некоторое разъяснение того, что означает этот термин, текст имеет тенденцию сосредоточиваться на проблемах и задачах или на обещании сделать нашу жизнь лучше. Некоторые утверждают, что интернет вещей приведет к неизбежной эволюции нашего общества, поскольку мы с каждым днем становимся все более связанными с окружающим миром. Однако, чтобы начать работу с интернетом вещей, вы можете избежать погружения в такие опрометчивые концепции или риторические повторения. Благодаря усилиям многих разработчиков и поставщиков открытого исходного кода вы фактически можете исследовать интернет вещей без интенсивного обучения, дорогостоящего оборудования и программного обеспечения. Самое приятное то, что вы можете исследовать интернет вещей, не изучая углубленно программирование и не тратя месяцы на изучение кода! Эта книга призвана стать руководством, которое поможет вам понять интернет вещей и начать создавать практические решения. Вы сможете использовать ее, чтобы узнать об интернете вещей еще больше. Поскольку эта книга для начинающих, мы рассмотрим язык программирования и среду, а затем подробно рассмотрим аппаратное обеспечение. Мы также изучим базовые сведения в области электроники, а потом разберем несколько проектов, которые помогут нам понять, как работать с программным обеспечением. Окончательный проект объединит все аспекты, чтобы помочь понять интернет вещей и научиться писать собственное программное обеспечение для создания IoT-решений с использованием облачных сервисов. Мы будем делать все это с применением одного из самых простых языков программирования и простых в использовании плат микроконтроллеров с открытым исходным кодом.
24  Что такое интернет вещей? Итак, что же такое интернет вещей (IoT)1? Давайте начнем с объяснения того, чем он не является. Интернет вещей – это не новое устройство, проприе­ тарное программное обеспечение или какой-то новый аппаратный компонент, а также не новая маркетинговая схема, призванная продать вам больше того, что у вас уже есть, переименовав его и объявив «новым и улучшенным»2. Верно, что в IoT используются уже существующие технологии и методы, способ их применения и возможность доступа к решению из любой точки мира делают IoT захватывающей концепцией для изучения. Давайте обсудим, что такое интернет вещей. Суть интернета вещей – это взаимосвязанные устройства, которые генерируют данные наблюдений, фактов и т. д. и обмениваются ими, делая их доступными для всех. Несмотря на то что, похоже, предпринимаются некоторые маркетинговые усилия, направленные на то, чтобы сделать что-либо, подключенное к интернету, IoT-решением или устройством (что мало чем отличается от бесстыдной маркировки всего на свете «облаком»), реальные IoT-решения предназначены для того, чтобы сделать наши знания об окружающем нас мире более своевременными и актуальными, позволяя получать данные о чем угодно из любого места в любое время. Как вы можете себе представить, если бы мы подключили каждое устройство вокруг нас к интернету и сделали общедоступными данные всех этих устройств, ясно, что количество IoT-устройств легко могло бы превысить численность населения планеты, а количество данных быстро превзошло бы возможности всех систем хранения, кроме, может быть, самых крупных. Эти проблемы широко известны как «проблема адресации» и «проблема больших данных», и они являются двумя наиболее активными и обсуждаемыми темами в области интернета вещей. Однако суть интернета вещей заключается не в количестве данных, а в понимании мира вокруг нас. То есть мы можем использовать данные, чтобы улучшить наш мир и наше представление о нем. Интернет вещей и вы Как мы наблюдаем мир вокруг нас? Человеческое тело – это чудо гениального сенсорного аппарата, который позволяет нам видеть, слышать, пробовать на вкус и даже осязать все, с чем мы сталкиваемся. Наш мозг может хранить визуальные и слуховые события, вспоминая их по своему желанию. Интернет вещей воспроизводит многие из этих сенсорных возможностей и, следовательно, может стать расширением наших собственных способностей. IoT-решения могут записывать наблюдения в виде данных от одного 1 2 https://en.wikipedia.org/wiki/Internet_of_Things. – Прим. авт. На русском языке соответствует статья https://ru.wikipedia.org/wiki/интернет_вещей. – Прим. перев. Например, все вроде бы теперь «в облаке», когда на самом деле ничего не менялось. – Прим. авт.
Интернет вещей и вы  25 или нескольких датчиков и делать их доступными для просмотра кем угодно в любом месте через интернет. Датчики – это устройства, которые выдают аналоговые или цифровые значения. Затем мы можем использовать собранные данные, чтобы сделать выводы по изучаемому предмету. Это может быть простой контактный датчик, определяющий открытие двери, окна или почтового ящика. В случае с контактами в почтовом ящике знания, которые мы получаем от простого их замыкания или размыкания (в зависимости от того, как датчик реализован и интерпретируется), могут использоваться для прогнозирования того, когда придет входящая почта или когда будет отправлена исходящая почта. Я использую термин «прогнозирование», потому что датчик (переключатель) сообщает нам только о том, что дверца была открыта или закрыта, а не о том, что что-то было помещено в почтовый ящик или удалено из него – для этого потребуются дополнительные датчики. Более сложный пример – использование серии датчиков для записи атмо­ сферных данных, таких как температура, влажность, атмосферное давление, скорость ветра, окружающее освещение, осадки и т. д., для мониторинга погоды, что позволяет нам выполнять анализ данных для прогнозирования погодных тенденций. То есть мы можем с разумной уверенностью предсказать, когда в этом районе выпадут осадки. Теперь добавьте возможность видеть эти данные не только в режиме реального времени (по мере их возникновения), но и удаленно из любой точки мира, и решение станет чем-то большим, чем простая метеостанция. Это становится способом наблюдать за погодой в данном месте из любой точки мира. Этот пример может показаться банальным, поскольку вы можете настроиться на любое количество теле-, интернет- и радиопередач, чтобы услышать погоду в любой точке мира. Но подумайте о последствиях создания такого решения в конкретном здании. Теперь вы можете видеть данные о погоде у себя дома! Таким же образом, но, возможно, в меньшем масштабе, мы можем создать решения для мониторинга состояния растений, которые помогут понять, как часто им нужна вода и другие питательные вещества. Или, возможно, мы сможем следить за нашими домашними животными, пока мы на работе. Кроме того, мы можем записывать данные о дикой природе в нашем районе, чтобы лучше понять наше влияние на нее. Интернет вещей – это больше, чем просто подключение к интернету Если устройство подключено к интернету, делает ли это его IoT-решением? Это зависит от того, кого вы спрашиваете. Некоторые считают, что ответ – да. Однако другие (например, я) утверждают, что ответ – нет, если от этого не будет какой-то выгоды. Например, если вы подключили тостер к интернету,
26  Что такое интернет вещей? какая от этого польза? Было бы бессмысленно (или по крайней мере крайне эксцентрично) получить на телефон сообщение от тостера о том, что тост готов. Итак, в данном случае ответ – нет. Однако если у вас есть такие члены семьи, как подростки или, возможно, пожилые люди, за которыми вы хотели бы следить, возможно, будет полезно проверить, как часто и когда они используют свой тостер. То есть эти данные могут вам помочь принять решения об адекватности и безопасности их действий. На мой взгляд, если данные бесполезны, независимо от того, просматриваются они в реальном времени или сохраняются для последующей обработки, то простое подключение их к интернету не делает их IoT-решением. Должна быть какая-то выгода от использования устройства таким образом. Само по себе подключение к интернету не является чем-то, заслуживающим названия IoT. Скорее, IoT-решения должны представлять собой вещи, обретающие некоторый смысл в пользе, пусть даже небольшой, кому-то или какому-то другому устройству либо услуге. Что еще более важно, для чего бы мы ни создавали решения интернета вещей, они позволяют нам ощущать мир вокруг нас и учиться на основе этих наблюдений. Самая сложная часть заключается в том, как данные собираются, хранятся и представляются. Мы увидим это на практике на примерах в последующих главах. Во врезке приведен пример спорного IoT-устройства – обычного бытового прибора. Однако IoT-решения часто могут использовать преимущества компаний, предоставляющих сервисы, которые могут помочь добавить или улучшить функциональность IoT-устройств. Эти сервисы обычно называются IoT-сер­ висами и предлагаются в диапазоне от простого хранения и предоставления доступа до инфраструктурных услуг, таких как хостинг. Интернет-техника: маркетинговый хайп? Одной из идей или концепций, которая, кажется, становится популярной, является подключение крупной бытовой техники к интернету. Хотя производители, возможно, хотят, чтобы вы поверили, что это новое и захватывающее устройство интернета вещей, на самом деле это не новая идея и не меняющее мир решение интернета вещей. Мне посчастливилось принять участие в семинаре по дизайну, проходившем в кампусе Microsoft в конце 1990-х годов. Во время экскурсии по кампусу нас познакомили с первым в мире холодильником с подключением к интернету (также называемым «умным холодильником» или просто интернет-холодильником)1. На полках были установлены датчики, определяющие вес продуктов. Можно было, проявив немного изобретательности, использовать датчики для уведомления вашего бакалейщика, когда у вас заканчиваются запасы молока, что позволило бы людям совершать покупки не только онлайн, но и автоматически. 1 https://en.wikipedia.org/wiki/Internet_refrigerator. – Прим. авт. Соответствующая статья на русском: https://ru.wikipedia.org/wiki/Смарт-холодильник. – Прим. перев.
Интернет вещей и вы  27 Сейчас, почти 25 лет спустя, мы видим, как производители создают холодильники, подключаемые к интернету. Однако, в отличие от первого умного холодильника, эти новые устройства могут стать центром внимания домохозяйств в социальных сетях. Многие не предоставляют никаких значимых данных о содержимом холодильника, за исключением возможности видеть видеоизображение содержимого полок на экране смартфона, что можно было бы решить, просто установив стеклянную дверцу. Достаточно сказать, что такие энтузиасты интернета вещей, как я, ломают голову над тем, как что-то подобное может быть полезным, а тем более хорошо продаваться. К сожалению, эти новые интернет-холодильники действительно хорошо продаются, но мне интересно, поддались ли потребители этой шумихе. Чтобы получить интересный комментарий о том, почему интернет-холодильник не для вас, поройтесь в Google, и вы найдете множество мнений – в основном негативных (и тем не менее люди все равно покупают эти вещи)1. Давайте оценим интернет-холодильник, исходя из моего определения интернета вещей: улучшает ли он вашу жизнь, предоставляя вам информацию об окружающем мире? Что ж, если вам нужно проверить, сколько у вас молока, находясь за 3000 миль от дома, то я думаю, это может быть полезно, но для многих из нас, кто предпочитает просто открыть дверцу и посмотреть, прежде чем пойти в магазин, возможно, это не является IoT-устройством. IoT-сервисы К сожалению, есть компании, которые рекламируют продукты «интернета вещей», являющиеся не чем иным, как маркетинговой шумихой – очень похоже на то, что некоторые компании сделали, добавив к названию сервиса слово «облако» или «облачный». Тем не менее есть несколько хороших продуктов и услуг, созданных специально для интернета вещей. Они варьируются от хранения данных и хостинга до специализированного оборудования. Действительно, компании добавляют IoT-услуги в свои продуктовые предложения быстрее, чем кто-либо может идти в ногу с последними новинками. И это не обычно подозреваемые в такой деятельности интернет-гиганты. Я видел решения и услуги интернета вещей, предлагаемые Cisco, AT&T, HP, а также бесчисленным количеством стартапов и небольших предприятий. Для описания тех компаний, которые предоставляют услуги для IoT-решений, я использую термин «поставщик IoT». Вам может быть интересно, что это за услуги и продукты и почему стоит рассмотреть возможность их использования. То есть что такое IoT-услуга и почему вы решили ее приобрести? Основная причина, по которой вы можете принять решение о покупке, связана с ее стоимостью и временем выхода на рынок. Если у ваших разработчиков нет ресурсов или недостаточно опыта и их получение потребует больше, чем стоимость услуги, возможно, выгоднее при1 www.howtogeek.com/260896/why-buying-a-smart-fridge-is-a-dumb-idea/. – Прим. авт.
28  Что такое интернет вещей? обрести услугу. Однако при принятии решения вам также следует учитывать дополнительные изменения в программном или аппаратном обеспечении (т. е. переоборудование вашего бизнеса). Однажды я столкнулся с продуманной и хорошо документированной контрактной услугой, которая позволяла продукту выйти на рынок раньше, чем предполагалось, с огромной экономией. К сожалению, несмотря на то что сторонники этого контракта получили награды за технические достижения, они не учли того факта, что все системы необходимо было переоборудовать для использования новой услуги. Точнее, внедрение нового сервиса заняло больше времени, чем написание его с нуля. Таким образом, вместо того чтобы экономить деньги, организация потратила почти в три раза больше и опоздала на рынок. Понятно, что нужно учитывать все факторы. Аналогичным образом, если у вас мало времени или у вас есть жесткие сроки для подготовки к производству вашего решения, возможно, быстрее будет приобрести услугу, а не создавать или адаптировать свою собственную. Это может потребовать немного больше затрат, но в данном случае мотивацией является время, а не затраты. Конечно, это смесь затрат и времени. Итак, какие IoT-услуги доступны? Ниже перечислены некоторые из них, появившиеся за последние несколько лет. Вероятно, по мере развития IoTрешений и услуг будет предлагаться больше.   Хостинг и представление корпоративных IoT-данных: услуги, которые позволяют вашим пользователям разрабатывать корпоративные IoTрешения, включая подключение, управление и настройку представления данных в удобной форме, такой как графики, диаграммы и т. д.   Хранение IoT-данных: сервисы, которые позволяют хранить данные и получать простые отчеты.   Сеть: услуги, предоставляющие сетевые и аналогичные протоколы связи или платформы для интернета вещей. Большинство из них специализируются на межмашинных услугах (M2M).   Аппаратные платформы интернета вещей. Поставщики, которые позволяют вам быстро разрабатывать и создавать прототипы IoTустройств, используя аппаратную платформу, а также множество поддерживаемых ей модулей и инструментов для создания устройств – от простого компонента до готового продукта. Теперь, когда мы знаем больше о том, что такое интернет вещей, давайте рассмотрим несколько примеров IoT-решений, чтобы лучше понять, на что они способны и как используются. Краткий обзор IoT-решений IoT-решение – это просто набор устройств, предназначенных для создания, представления или получения данных о каком-либо событии (серии событий) или наблюдений. Сюда могут входить устройства, которые генерируют данные, такие как датчики, устройства, которые объединяют данные для
Интернет вещей и вы  29 получения каких-либо выводов, устройства или службы, предназначенные для сведения в таблицы и хранения данных, а также устройства или системы, предназначенные для представления данных. Любое из них может быть подключено к интернету. IoT-решения могут обладать несколькими из этих качеств, независимо от того, объединены ли они в одно устройство, такое как веб-камера, в совокупность устройств – пакет датчиков или блок мониторинга, такой как метеостанция, или в сложную систему специализированных датчиков, агрегаторов, хранилищ данных с презентацией, такую как полная система домашней автоматизации. На рис. 1.1 показана футуристическая картина всех устройств, повсюду подключенных к интернету: к базам данных, сборщикам или интеграторам данных, службам отображения и другим устройствам. Рис. 1.1  Сеть интернета вещей Давайте рассмотрим несколько примеров IoT-решений. Примеры, описанные в этом разделе, представляют собой сочетание решений, которые должны дать вам представление о размерах и сложности интернета вещей. Отметим также, что некоторые из этих решений используют IoT-услуги от поставщиков. Сенсорные сети Сенсорные сети являются одной из наиболее распространенных форм IoTрешений. Проще говоря, сенсорные сети позволяют вам наблюдать за миром вокруг вас и понимать его. Сенсорные сети могут принять форму системы мониторинга пруда, которая предупреждает вас об уровне воды, ее чистоте (загрязнении) или температуре, обнаруживает хищников или даже автоматически включает такие функции, как освещение или кормление рыб в вашем садовом пруду.
30  Что такое интернет вещей? Если вы или кто-то из ваших знакомых провели какое-то время в медицинском учреждении, скорее всего, могли наблюдать, как использовалась сенсорная сеть для мониторинга функций тела, таких как температура вашего тела, частота сердечных сокращений, дыхательная способность или даже диапазон движений ваших конечностей. Современные автомобили также содержат сенсорные сети, предназначенные для мониторинга двигателя, климата и даже в некоторых автомобилях дорожных условий. Например, функция предупреждения о полосе движения использует датчики (обычно камеру, микропроцессор и программное обеспечение), чтобы обнаружить, когда вы слишком далеко отклоняетесь к разметке полосы движения или дороги. Производственные предприятия также используют сенсорные сети для мониторинга и управления машинами, конвейерами и т. д. Информационные центры по доставке тоже используют сенсорные сети, которые помогают направлять посылки в нужные контейнеры и, в конечном итоге, в нужные грузовики или самолеты для перевозки. Таким образом, в сенсорных сетях используются один или несколько датчиков, которые проводят измерения (наблюдения) за событием или состоянием и передают эти данные другому компоненту или узлу в сети, которые затем представляются в той или иной форме для анализа. Давайте рассмот­ рим пример важного медицинского IoT-решения. Медицинские приложения Медицинские приложения, включая мониторинг здоровья и фитнес-мониторинг, привлекают большое внимание в качестве потребительских товаров. Эти решения охватывают широкий спектр возможностей, от фитнесфункций, встроенных в новые Apple Watch, до фитнес-браслетов, которые отслеживают тренировки, и медицинских приложений, которые помогают контролировать даже опасные для жизни состояния. Например, есть решения, которые могут помочь вам справиться с диабетом. Диабет – это болезнь, от которой страдают миллионы людей во всем мире. Существует несколько его форм: диабет типа 1 и типа 2. Наиболее серьезным является тип 1, но тип 2 также может быть серьезным, если его не лечить должным образом. Люди, страдающие диабетом типа 1, не производят достаточного количества (или вообще не производят) инсулина из-за генетических недостатков, врожденных дефектов или повреждений поджелудочной железы. Инсулин – это гормон, который организм использует для извлечения из крови простого сахара, называемого глюкозой, который создается из сахаров и крахмалов для использования в клетках. Если не следить за уровнем сахара в крови, это может привести к его опасно низкому или высокому уровню; оба могут быть опасными для жизни и, если их не контролировать, могут вызвать долгосрочное повреждение внутренних органов, нервов и других областей. Это самое серьезное состояние. Больные диабетом типа 1 должны постоянно контролировать уровень глюкозы в крови, чтобы убедиться, что они правильно принимают лекарства
Интернет вещей и вы  31 (в первую очередь инсулин), ведут сбалансированный здоровый образ жизни и соблюдают диету. Если уровень глюкозы в крови упадет слишком низко или взлетит слишком высоко, у них может возникнуть множество неприятных симптомов. Хуже того, экстремально низкий уровень глюкозы в крови очень опасен и может привести к летальному исходу. Одна из новейших версий глюкометра (прибора для измерения уровня глюкозы в крови) состоит из небольшого датчика, который остается в организме на неделю, а также монитора, который подключается к датчику через Bluetooth. Вы носите монитор на своем теле или всегда держите его на расстоянии не более 20 футов. Решение продается компанией Dexcom и называется непрерывным монитором глюкозы (CGM), которое позволяет пациенту делиться своими данными через телефон. Таким образом, пациент соединяет свою CGM со своим телефоном, а затем делится данными через интернет с другими. Это могут быть близкие люди, те, кто помогает им в уходе, или медицинские работники. На рис. 1.2 показан пример монитора и датчика Dexcom CGM. Монитор находится слева, а датчик и передатчик – справа. Датчик размером с небольшую иглу шприца остается в организме на срок до недели. Рис. 1.2  Монитор глюкозы Dexcom с датчиком Функция Dexcom Share позволяет пациенту сделать свои данные доступными другим через приложение на телефоне. То есть телефон пациента передает данные на облачные серверы Dexcom, которые затем отправляются всем, у кого есть приложение Dexcom Share и есть разрешение на просмотр данных. На рис. 1.3 показан пример отчета Dexcom Share CGM из приложения Dexcom Share iOS, которое позволяет легко и быстро проверить уровень глюкозы в крови друга или родственника. Приложение не только позволяет визуализировать данные, но также может передавать оповещения о низком или высоком уровне глюкозы в крови, имеющем серьезные последствия для пациентов, страдающих дополнительными заболеваниями или осложнениями диабета. Например, если уровень глюкозы в крови пациента падает, когда он один, недееспособен или не может получить лечение, близкие с помощью приложения Dexcom Share могут отреагировать, проверив пациента и потенциально избежав критического диабетического события.
32  Что такое интернет вещей? Хотя это решение представляет собой один датчик, подключенный к интернету через собственное приложение, оно является отличным примером медицинского IoT-устройства, которое может улучшить жизнь не только пациента, но и всех, кто о нем заботится. Dexcom также предоставляет бесплатное приложение для Windows под названием Dexcom Studio (http://dexcom.com/dexcom-studio), которое позволяет пациентам просматривать данные, собранные их мониторами, и создавать отчеты, которые могут использоваться для отслеживания уровня глюкозы в динамике. Отчеты включают средние значения, закономерности, ежедневные тенденции и многое другое. Они также могут поделиться данными со своим врачом. На рис. 1.4 показан пример Dexcom Studio с типичными загруженными данными. Рис. 1.3  Отчет в приложении Dexcom Share Рис. 1.4  Dexcom Studio
Интернет вещей и вы  33 А как насчет тестеров глюкозы в крови – глюкометров? До появления таких решений, как Dexcom CGM, диабетикам приходилось использовать ручной тестер. Традиционные тестеры для измерения уровня глюкозы в крови представляют собой одноразовые устройства, требующие от пациента проколоть палец или руку и набрать небольшое количество крови на тест-полоску. Хотя это устройство используется уже много лет, только недавно производители начали выпускать тестеры уровня глюкозы в крови с функциями памяти и даже возможностью подключения к другим устройствам, таким как ноутбуки или телефоны. Окончательной эволюцией этих устройств является такое решение, как Dexcom, которое стало медицинским IoT-устройством, улучшающим качество жизни диабетиков. В сочетании с программируемыми оповещениями вы и ваши близкие можете помочь справиться с последствиями диабета. Если у вас есть близкий человек, страдающий диабетом, CGM стоит каждой копейки только ради душевного спокойствия. Это истинная сила интернета вещей, воплощенная в потенциально спасительном решении. Автомобильные IoT-решения Еще одним распространенным примером интернета вещей является подключение к интернету автомобильных функций. Один из старейших продуктов называется OnStar и доступен на большинстве последних моделей автомобилей General Motors (GM). Хотя OnStar представляет собой спутниковую службу, имеющую несколько уровней и множество платных опций, она включает в себя интернет, позволяющий общаться с владельцами транспортных средств. Действительно, новейшие автомобили GM оснащены встроенной в автомобиль точкой доступа Wi-Fi! Более того, есть некоторые базовые функции, которые владельцы GM могут бесплатно использовать и которые, на мой взгляд, очень ценны. Бесплатные базовые функции включают в себя регулярные отчеты о техническом обслуживании, отправляемые вам по электронной почте, а также возможность использовать приложение на телефоне для удаленной блокировки/разблокировки и удаленного запус­ ка – все функции обычного брелока удаленно. Это классная функция, если вы когда-нибудь запирали ключи в машине! На рис. 1.5 показан пример приложения удаленного брелока на Рис. 1.5  Функции брелока iOS. Конечно, за дополнительную плату доприложения OnStar
34  Что такое интернет вещей? ступно еще больше функций, включая навигацию, телефон, Wi-Fi и техподдержку по вызову. Приложение OnStar работает, подключаясь к облачным службам OnStar и запрашивая функцию (например, разблокировку), которая отправляется на автомобиль через спутниковую сеть OnStar. Таким образом, это отличный пример того, как решения IoT используют несколько протоколов связи. Больше всего мне нравится функция отчетов о техническом обслуживании. Вы получите электронное письмо с обзором технического состояния вашего автомобиля. В отчет включены такие данные, как срок службы масла, давление в шинах, предупреждения двигателя и трансмиссии, выбросы, подушки безопасности и многое другое. На рис. 1.6 показан фрагмент типичного электронного письма, которое вы получаете. Рис. 1.6  Отчет о техническом обслуживании OnStar
Интернет вещей и вы  35 Обратите внимание на отображаемую информацию. Фактические данные передаются в OnStar с вашего автомобиля. Например, показания одомет­ра и данные о давлении в шинах берутся непосредственно из бортового хранилища данных автомобиля. То есть данные с датчиков считываются и интерпретируются, и вам формируется отчет. Эта функция демонстрирует, как автоматическая компиляция данных в IoT-решении может помочь нам поддерживать автомобили в хорошем состоянии, заблаговременно предупреж­ дая о необходимости технического обслуживания, и, следовательно, иметь высокую стоимость при перепродаже. Должен отметить, что GM – не единственный производитель автомобилей, предлагающий подобные услуги. Многие другие работают над собственными решениями, начиная от набора функций, подобных OnStar, и заканчивая решениями, ориентированными на развлечения и возможности подключения к другим сервисам. Управление транспортным парком Еще одним примером интернета вещей является система управления транспортным парком. Хотя системы управления, например, автопарком были разработаны и внедрены задолго до появления термина «интернет вещей», сейчас они позволяют предприятиям контролировать свои автомобили, грузовики и корабли – практически любое движущееся устройство, – чтобы не только отслеживать их текущее местоположение, но и использовать данные о местоположении (координаты GPS, полученные в реальном времени) для планирования более эффективных маршрутов, тем самым снижая стоимость доставки. Системы управления транспортным парком предназначены не только для маршрутизации. Они также позволяют предприятиям контролировать каждую единицу техники для проведения диагностики. Например, можно узнать, сколько топлива находится в каждом грузовике, когда проводилось его последнее ТО или, что более важно, когда наступит срок следующего ТО и многое другое. Комбинация географического отслеживания транспортных средств и диагностики называется телематикой. На рис. 1.7 показана схема системы управления транспортным парком. На рисунке вы видите применение систем GPS для отслеживания местоположения, а также спутниковую связь для передачи дополнительных данных, таких как диагностика, состояние полезной нагрузки и т. д. Все это в конечном итоге передается через интернет, и данные становятся доступными бизнес-аналитикам. Вы можете подумать, что системы управления транспортным парком предназначены только для крупных судоходных компаний, но с распространением модулей GPS и рынка микроконтроллеров любой может создать подобную систему управления. То есть их разработка не требует миллионов долларов.
36  Что такое интернет вещей? Рис. 1.7  Пример управления транспортным парком Например, если вы владелец компании по доставке, вы можете легко установить модули GPS с сотовой или беспроводной связью на велосипед каждого курьера, чтобы отслеживать его местоположение, среднее время в пути и многое другое. Более конкретно, вы можете использовать такое решение для минимизации времени доставки, позволяя передавать посылки от одного курьера к другому, а не возвращать их на склад каждый раз, когда они завершают набор доставок. Дроны с камерами и интернет вещей Одним из возможных вариантов использования интернета вещей является предоставление через интернет доступа к данным, генерируемым дронами. Некоторые могут подумать, что дроны – это вторжение в частную жизнь, и я согласен с этим в ситуациях, когда ими злоупотребляют или нарушаются установленные законы. К счастью, подавляющее большинство владельцев дронов подчиняются местным законам, правилам и пожеланиям владельцев недвижимости. Однако существует множество законных вариантов использования дронов, будь то наземное, воздушное или морское базирование. Например, я могу представить решения для домашнего мониторинга, с помощью которых вы можете удаленно проверять свой дом, просматривая данные со стационарных камер вместе с данными с мобильных дронов. Я, например, хотел бы увидеть решение, которое позволило бы мне запрограммировать заранее определенную траекторию полета часового для наблюдения за моим имуществом с помощью летающего дрона с камерой.
Интернет вещей и вы  37 Хотя у некоторых производителей есть дроны с поддержкой Wi-Fi, существует не так много доступных вариантов потребительского уровня, передающих данные в реальном времени через интернет. Однако есть варианты, позволяющие публиковать видео и фотографии прямо с дрона. Один из моих дронов – Yuneec Breeze (www.yuneec.com/en_US/products/breeze/overview.html), позволяет мне публиковать фотографии и видео, снятые с дрона, в социальных сетях. Интересно, что эти дроны называются «селфи-дронами», поскольку у них есть функции, которые позволяют работать в автономном режиме, в том числе режим, в котором дрон следует за вами, снимая ваши действия или кружа вокруг вашего местоположения. Хотя эти новые дроны требуют ручного действия для публикации данных, появление IoT-решений реального времени, включающих в себя дроны, является лишь вопросом времени. Конечно, нынешние разногласия и движение правительств в сторону регистрации и отслеживания дронов наряду с ужесточением ограничений на их использование могут помешать распространению дронов и IoT-решений с данными, полученными с помощью дронов. Интернет вещей и безопасность Недавняя серия массовых утечек данных доказывает, что обеспечить базовую безопасность недостаточно. Мы видели все: от прямой кражи до использования данных, украденных у очень известных компаний, таких как Target (возможно, было скомпрометировано более 40 млн номеров кредитных карт) и государственных учреждений, таких как Управление кадров США (скомпрометировано более 20 млн номеров социального обеспечения). Решения интернета вещей не застрахованы от угроз безопасности. Действительно, по мере того как IoT-решения все больше и больше интегрируются в нашу жизнь, то же самое происходит и с нашими личными данными. Таким образом, к безопасности следует относиться крайне серьезно и закладывать ее в решение с самого начала. Сюда входят решения, которые мы разрабатываем сами. Точнее, если вы проектируете метеостанцию для собственного использования, вам следует принять разумные меры для обеспечения защиты данных как от случайного, так и от преднамеренного использования. Вы можете подумать, что данные о погоде не представляют высокого риска, но рассмотрите случай, когда вы включаете координаты GPS для своих датчиков (разумная функция), чтобы люди могли видеть, где наблюдается эта погода. Если кто-то сможет увидеть эту информацию и определить, что решение использует подключение к интернету, вполне возможно, что он сможет получить физический доступ к интернет-устройству и, возможно, использовать его для дальнейшего проникновения в ваши системы. Таким образом, безопасность касается не только данных; оно должно охватывать все аспекты решения: от данных до программного обеспечения, аппаратного обеспечения и физического доступа. Есть четыре области, в которых вам, возможно, стоит подумать о том, чтобы уделить особое внимание обеспечению надежной защиты вашего IoT-
38  Что такое интернет вещей? решения. Как вы увидите, это включает в себя несколько вещей, которые вам следует учитывать в отношении существующей инфраструктуры, компьютеров и даже привычек безопасной работы на компьютере. Используя эти области, вы создадите многоуровневый подход к безопасности: часто называемый методом глубоко эшелонированной защиты. Нужно ли мне действительно беспокоиться о безопасности? Если вам интересно, почему я включил этот раздел в книгу по интернету вещей и языку Python для начинающих, задумайтесь, что вы в конечном итоге хотите делать со знаниями, полученными из этой книги. Если вас интересует только изучение того, как работать с вашей новой платой MicroPython, и у вас нет стремления к разработке чего-то большего, то вы можете просмотреть эти разделы. Однако если в ваши цели входит создание развертываемых IoT-решений, особенно если вы планируете подключить их к интернету, следует учитывать безопасность вашего решения. В любом случае, я настоятельно рекомендую прочитать последующий текст и следовать советам по обеспечению безопасности ваших IoT-решений. Безопасность начинается дома Прежде чем внедрять решение интернета вещей в свою домашнюю сеть, вам следует принять меры предосторожности, чтобы обеспечить защиту компьютеров в домашней сети. Это важно, потому что если кто-то получит доступ к вашей домашней сети, он может совершать всевозможные деструктивные действия. Самая распространенная ошибка – не обеспечить безопасность домашней сети Wi-Fi. Это не только означает, что соседи могут подключиться к вашей сети и использовать оплаченный вами трафик, но также означает, что они могут получить доступ к системам в домашней сети, сделав ваши IoT-устрой­ ства, компьютеры, гаджеты и т. д. уязвимыми для атак. К счастью, существует несколько рекомендаций по обеспечению безопасности домашней сети, которые помогут снизить эти риски. К ним относятся следующие:   используйте пароли. Это может показаться простой задачей, но всегда следует убедиться, что пароли заданы на всех ваших компьютерах и устройствах. Кроме того, выработайте хорошие привычки в отношении паролей, такие как требование использования более длинных строк, смешанного регистра, цифр и символов, чтобы пароли было нелегко угадать;   защитите свой Wi-Fi. Если у вас есть сеть Wi-Fi, обязательно добавьте пароль и используйте новейшие протоколы безопасности, такие как WPA2 или, что еще лучше, встроенные функции безопасной настройки некоторых беспроводных маршрутизаторов;
Интернет вещей и вы  39   используйте брандмауэр. Вам также следует использовать брандмауэр, чтобы заблокировать все неиспользуемые порты (TCP или UDP). Например, заблокируйте все порты, кроме тех, которые использует ваше решение (как, например, порт 80 для html);   ограничьте физический доступ: просто запирайте двери! Пусть у вашей сети отличный пароль, а ваши компьютеры используют зашифрованный биометрический доступ против супершпионажа, но эти вещи окажутся бессмысленны, если кто-то может получить доступ к вашему сетевому оборудованию напрямую. Для IoT-решений это означает, что любые внешние компоненты должны быть установлены в защищенных от несанкционированного доступа корпусах или спрятаны, чтобы их нельзя было обнаружить. В этот перечень также входит любая сетевая проводка. Вы, возможно, знаете, как выполнить некоторые из этих действий, но для других может потребоваться помощь более компетентных друзей, лучше разбирающихся в устройствах и сетях. Например, если вы не знаете, что такое брандмауэр, попросите кого-нибудь вам помочь. Немного дополнительной безопасности стоит усилий, потраченных на изучение основ настройки брандмауэра. Защитите свои устройства Как упоминалось ранее, ваши IoT-устройства также должны быть защищены. Некоторые практики, которые следует иметь в виду, включают следующее:   используйте пароли. Всегда добавляйте пароли к учетным записям пользователей на любом устройстве, на котором установлена операционная система. Это включает в себя необходимость переименовать все пароли, установленные по умолчанию. Например, у вас может возникнуть соблазн посчитать крошечный Raspberry Pi слишком маленьким устройством, чтобы представлять угрозу безопасности, но если вы примете во внимание, что эти устройства работают под управлением одной из самых мощных операционных систем (разновидности Linux), Raspberry Pi может стать очень мощным хакерским инструментом;   поддерживайте актуальность программного обеспечения. Старайтесь использовать последние версии любого установленного программного обеспечения. Сюда входит операционная система, а также любые встроенные программы или инструменты программирования, которые вы можете использовать. Новые версии часто имеют улучшенную безопас­ность или меньшее количество уязвимостей;   если программное обеспечение предлагает функции безопасности, используйте их. Если на ваших устройствах работают серверы или службы,
40  Что такое интернет вещей? которые предлагают такие функции, как автоматическая блокировка неверно введенных паролей, включите их. Не каждая программа имеет эти функции, но если они доступны, то могут стать отличным способом отражения повторяющихся атак. Используйте шифрование Это одна из областей, которую часто упускают из виду. Хотя данный вариант обычно используется только решениями, передающими конфиденциальные данные, такими как коммерческие IoT-устройства, если вы планируете отправлять данные, которые, по вашему мнению, являются конфиденциальными, вы можете дополнительно защитить их, если использовать шифрование как при хранении, так и во время передачи данных. Если вы зашифруете свои данные, даже если кто-то получит физический доступ к устройству хранения, данные будут бесполезны, поскольку они не могут быть легко расшифрованы. Будьте так же осторожны с ключами шифрования и паролями, как и с паролями вашего компьютера. Безопасность не ограничивается облаком Существует множество соображений по подключению устройств интернета вещей к облачным сервисам. Действительно, Microsoft и другие компании упростили использование облачных сервисов для IoT-решений. Однако есть два важных фактора, касающихся безопасности данных интернета вещей:   вам нужно облако? Первое, о чем вам следует подумать, – нужно ли вам помещать какие-либо данные в облако. Зачастую облачные сервисы позволяют очень легко хранить и просматривать ваши данные, но так уж ли необходимо это делать? Например, вы можете быть очень заинтересованы в просмотре логистических данных о том, где ваша собака проводит время, пока вы на работе, но кто еще захочет просмотреть эти данные? В этом случае хранить данные в облаке, с тем чтобы сделать их доступными для всех, нет необходимости;   не расслабляйтесь! Многие люди теряют бдительность при работе с облачными сервисами. По какой-то причине они считают облако более безопасным. Дело в том, что это не так! Фактически при работе в облаке вы должны применять те же самые лучшие методы обеспечения безопасности, которые применяете к своей собственной сети и компьютерам. Действительно, во всяком случае вам нужно быть еще более бдительными, потому что облачные сервисы не находятся под вашим контролем в отношении защиты от физического доступа (пусть и удаленного и маловероятного), и вы не можете гарантировать, что данные не находятся на тех же устройствах, что и данные десятков, сотен или даже тысяч других пользователей.
Python и интернет вещей  41 Теперь, когда у нас есть представление о том, как следует учитывать безопас­ность в ваших проектах, давайте кратко рассмотрим язык программирования, который мы будем использовать в этой книге, – Python. Python и интернет вещей Python – это интерпретируемый объектно ориентированный язык сценариев высокого уровня. Одним из главных преимуществ Python является наличие четкого и легкого для понимания синтаксиса, который читается как можно ближе к английскому. То есть вы можете читать скрипты Python и понимать их, даже если не изучали Python вовсе. В Python также меньше знаков препинания (специальных символов) и синтаксических манипуляций, чем в других языках. Вот несколько ключевых особенностей Python:   интерпретатор обрабатывает Python во время выполнения. Компилятор не используется1;   Python поддерживает конструкции объектно ориентированного программирования посредством классов и методов;   Python – отличный язык для программистов начального уровня, поддерживающий разработку широкого спектра приложений;   Python – это язык сценариев, но его можно использовать для широкого спектра приложений;   Python очень популярен и используется во всем мире, что обеспечивает ему огромную базу поддержки;   В Python мало ключевых слов, простая структура и четко определенный синтаксис. Это позволяет начинающему быстро освоить язык. История Python Python был разработан Гвидо ван Россумом в конце 1980-х – начале 1990-х го­ дов в Национальном научно-исследовательском институте математики и информатики Нидерландов и в основном поддерживается командой разработчиков этого института. Он был создан на основе многих языков, включая Modula-3, C, C++ и языки сценариев оболочки Unix. Интересный факт о Python: он был назван в честь шоу BBC «Летающий цирк Монти Питона» и не имеет ничего общего с рептилией с таким же именем. Цитирование Монти Питона в документации по исходному коду (и даже юмористическое отвлечение сообщений об ошибках) очень распространено, 1 О различиях компиляторов и интерпретаторов см. врезку на стр. 52. – Прим. перев.
42  Что такое интернет вещей? и хотя некоторые профессиональные разработчики могут раздражаться от таких инсинуаций, «питонщики» считают это демонстрацией вашего авторитета в Python. Если вам нравится «Монти Питон», я советую вам использовать в своем коде фрагменты из шоу. Одно из занятий, которые мне нравятся в качестве развлечений, – это вывод сообщений. Мое любимое выглядит примерно так1: > DUPLICATE FILE ERROR: He says they've already got one! Некоторые могут задаться вопросом, как такой язык, как Python, может быть полезен при написании IoT-решений. Ответом на этот вопрос является крутой продукт под названием MicroPython. Выражаясь кратко, MicroPython – это сжатый оптимизированный код Python версии 3, загруженный в аппаратное обеспечение. Это означает, что вместо необходимости запуска интерпретатора в операционной системе для выполнения кода Python микросхема, содержащая MicroPython, может запускать код Python непосредственно на оборудовании. Никакая операционная система не требуется. Фактически MicroPython имеет встроенный базовый файловый ввод-вывод. О MicroPython мы узнаем больше в следующей главе. Это замечательный новый вариант для тех, кто хочет изучить интернет вещей, но не хочет изучать сложный язык программирования или тратить много времени на изучение новых операционных систем, инструментов и оборудования. Однако сначала давайте посмотрим, насколько легко использовать MicroPython. Онлайн-симулятор MicroPython Для тех из вас, кто хочет ощутить, что значит использовать MicroPython для управления оборудованием, хорошие ребята из Wokwi (https://wokwi.com) соорудили интерактивный онлайн-симулятор IoT-проектов, дополненный моделированием электроники, такой как датчики и устройства вывода, подключаемой к одной из популярных IoT-платформ. В том числе можно запустить симулятор MicroPython для Raspberry Pi Pico (www.raspberrypi. com/products/raspberry-pi-pico/) – одной из самых популярных плат для этой цели. Посетив сайт Wokwi, вы найдете длинный список симуляторов интернета вещей, в которых используются различные платы. Если вы прокрутите вниз, то увидите набор симуляций проектов MicroPython IoT, как показано на рис. 1.8. Если вы нажмете кнопку More MicroPython Projects (Дополнительные проекты MicroPython), то увидите текущий список доступных проектов. 1 «ОШИБКА ДУБЛИКАТА ФАЙЛА: он говорит, что такой у них уже есть!» – Прим. перев.
Python и интернет вещей  43 Рис. 1.8  Симуляция проектов MicroPython IoT (Wokwi) Существует также симулятор MicroPython для Raspberry Pi Pico. Если вы прокрутите страницу вниз до раздела IoT, то увидите кнопку с именем + New Project (+ Новый проект), позволяющую создать новый проект, как показано на рис. 1.9. Рис. 1.9  Создание нового проекта симуляции MicroPython (Wokwi) После нажатия этой кнопки вам будет предложено выбрать плату для использования в симуляторе. Если вы следуете данному примеру, выберите Raspberry Pi Pico или Raspberry Pi Pico W (в примере далее подойдет любая из них). Симулятор откроется с новым пустым проектом, в котором слева показано окно кода, а справа – простой рисунок, изображающий выбранную плату.
44  Что такое интернет вещей? Когда вы запустите код, консоль MicroPython появится справа под изображением платы. Например, если вы выберете Raspberry Pi Pico, то увидите пустой проект, показанный на рис. 1.10. Рис. 1.10  Пустой проект симуляции MicroPython (Wokwi) Обратите внимание на три кнопки в верхней правой части демонстрационного окна. Первая – зеленый кружок с белой стрелкой. Нажатие этой кнопки запустит код, показанный в окне кода слева. Синий кружок со знаком плюс позволяет добавить новую деталь (электронный компонент). Также имеется функция, позволяющая создавать соединения с платой в виде проводников. Существует множество компонентов на выбор, и вам рекомендуется их опробовать, но для данного простого примера нам не нужны никакие компоненты. Наконец, серый кружок с тремя точками открывает меню, позволяющее увеличивать или уменьшать масштаб и выполнять аналогичные функции настройки. В примере, который мы собираемся проделать первым, используется только Raspberry Pi Pico. Точнее, на плате имеется управляемый пользователем светодиод (LED), который мы можем включать и выключать с помощью MicroPython. Код примера показан в лис­тин­ге 1.1. Листинг 1.1  Пример мигания на MicroPython from machine import Pin from utime import sleep print("MicroPython for the IoT: Blinky Example")
Python и интернет вещей  45 led = Pin(25, Pin.OUT) while True: led.on() sleep(0.5) led.off() sleep(0.5) Пример представляет собой вариант классического IoT-проекта «Hello, World!»1, но на данном этапе не очень важно знать, что и как делает код. Тем не менее если вы посмотрите на код, большую часть его достаточно легко понять. Задача изучения MicroPython – изучить синтаксис и специальные команды, подобные тем, которые показаны в первых двух строках. Теперь, когда у нас есть пример кода, давайте запустим симуляцию! Вы можете просто ввести строки кода в редактор кода слева. Когда будете готовы, щелкните зеленый кружок со стрелкой. Если вы ввели код без ошибок, справа должно появиться новое окно, в котором представлена консоль MicroPython с нашим приветствием, а также вы должны увидеть светодиод, расположенный наверху платы Raspberry Pi Pico, как показано на рис. 1.11. Вы увидите, как светодиод включается и выключается два раза в секунду. Рис. 1.11  Симуляция кода мигания (Wokwi) 1 См. на русском языке статью https://ru.wikipedia.org/wiki/Hello,_world!. – Прим. перев.
46  Что такое интернет вещей? Хотя вы, возможно, не до конца понимаете код Python в этом примере, я предлагаю вам потратить несколько минут на пробу, чтобы вы могли увидеть, насколько это увлекательно (и как мало кода для этого требуется) – управлять оборудованием с помощью MicroPython. По крайней мере симулятор должен вызвать у вас желание узнать больше о MicroPython. СОВЕТ Используйте ссылку https://wokwi.com/projects/new/micropython-pi-pico, чтобы перейти непосредственно к симулятору MicroPython для Raspberry Pi Pico. Вы также можете вернуться к этому симулятору, чтобы опробовать свой собственный код и более простые проекты без необходимости покупать дорогое оборудование. Если вы хотите создавать и хранить свои проекты на платформе Wokwi, то можете нажать кнопку Sign Up (Зарегистрироваться) в правом верхнем углу и следовать инструкциям, чтобы создать свою учетную запись. Получив учетную запись, вы сможете создавать и сохранять свои проекты в Wokwi, что позволит вам изучить все функции онлайн-симулятора. Прекрасно! Итоги Интернет вещей – это захватывающий новый мир для всех нас. Те из нас, кто молод сердцем, но достаточно взрослый, чтобы помнить сериал «Джетсоны», вспоминают, как видели то, что возможно в воображаемой стране. Говорящие тостеры, летающие машины и роботы, выскакивающие из портфелей, – как бы к этому не относились, телевизионные фантазии, возникшие десятилетия назад, становятся реальностью. У нас есть наручные часы, которые можно использовать как телефоны и видеоплееры; мы можем разблокировать наши машины, узнать, вышла ли наша собака на улицу, и даже открыть дверь с другого конца города. Все это возможно и работает сегодня с появлением интернета вещей. В этой главе мы узнали, что такое интернет вещей, и увидели несколько примеров известных решений. Мы также можем узнать, как Microsoft привлекает пользователей Windows, расширяя свою операционную систему Windows 10 для интернета вещей с помощью оборудования Raspberry Pi. Это очень интересная возможность для людей, не желающих углубляться в нюансы операционной системы на базе Linux, изучить мир аппаратного обеспечения и интернета вещей с привычной и понятной платформы. В следующей главе мы узнаем больше о MicroPython, в том числе о том, как на нем программировать. Как вы увидите, это несложно. Затем в главе 3 мы более подробно изучим аппаратное обеспечение, которое будем использовать для запуска MicroPython, чтобы завершить наш обзор начала работы с MicroPython для интернета вещей.
Глава 2 Знакомство с MicroPython Теперь, когда мы узнали больше об интернете вещей и видели демонстрацию, пришло время узнать больше о MicroPython – с чего начинать, как он работает и примеры того, что вы можете сделать с вашей собственной платой. Изучить MicroPython очень легко даже тем, у кого нет опыта программирования. Действительно, все, что вам нужно для изучения MicroPython, – это немного терпения и немного времени, чтобы привыкнуть к синтаксису и особым приемам работы с MicroPython, платами и электроникой. Как мы увидим, можно многое сделать, обладая лишь небольшими знаниями. В этой главе мы узнаем больше о MicroPython, включая обзор того, как начать работу с одной из самых популярных плат. Не волнуйтесь, если у вас еще нет платы; примеры в этой главе предназначены для того, чтобы дать вам представление о том, что вы можете сделать, а не для подробного руководства. Подробное руководство для платы, используемой в этой главе, а также для других плат мы увидим в главе 3. Мы также более подробно изучим программирование на Python в главе 4. ПРИМЕЧАНИЕ Я называю платы микроконтроллеров, на которых работает MicroPython или которые могут быть загружены двоичными файлами MicroPython, «платами, совместимыми с MicroPython» или «платами MicroPython». Давайте начнем с рассмотрения того, что такое MicroPython, в том числе почему он был создан и как начать. Начало работы Использование языка Python для управления оборудованием существует уже некоторое время. Пользователи Raspberry Pi, pcDuino и других недорогих компьютеров и подобных плат получили преимущество использования Python для управления оборудованием. В данном случае они использовали
48  Знакомство с MicroPython полные версии языка программирования Python в собственной операционной системе на базе Linux. Однако для этого требовались специальные библиотеки, созданные для взаимодействия с оборудованием. Эти библиотеки были разработаны для подключения оборудования через контакты (выводы) ввода/вывода общего назначения (GPIO, General Purpose Input/Output). Выводы GPIO обычно располагаются на плате в одном или нескольких рядах штыревых контактов на плате. На некоторых платах используются гнездовые контакты. Хотя эти платы позволяли действовать тем, кто хотел разрабатывать проекты в области электроники, пользователям требовалось покупать плату, а также периферийные устройства к ней, такие как клавиатура, мышь и монитор. Мало того, пользователям также приходилось изучать операционную систему. Для тех, кто не привык к Linux, это само по себе может стать проб­лемой. Возможно, вас интересуют микроконтроллеры, такие как чрезвычайно популярный Arduino или продукты Espressif, известные как платы ESP. Для программирования этих плат вы должны использовать C-подобный язык1, что может быть больше, чем некоторые готовы выучить. Идея MicroPython (Python для микро­ конт­роллеров) заключалась в том, чтобы объ­единить простоту изучения Python с низкой стоимостью и прос­тотой использования плат микроконтроллера, что позволило бы гораздо большему количеству людей работать с электроникой для художественных и научных проектов. Новичкам не придется изучать новую операционную систему или изучать один из наиболее сложных языков программирования2. На рис. 2.1 показан логотип MicroPython в виде значка от Adafruit. Это довольно круто, не так ли? Эта змея Рис. 2.1  Значок (питон) наклеивается на плату с интегральс логотипом MicroPython ной схемой. Если вам не к чему прикрепить (с разрешения adafruit.com) 1 2 Некоторые могут его назвать C++, и я полагаю, что в этом есть доля правды, но они больше похожи на C для общего базового использования. – Прим. авт. Автору трудно возразить по части изучения Linux (для микроконтроллерных устройств она в общем оказывается совершенно избыточной), но по поводу изуче­ ния языков программирования новичку следует иметь в виду, что существует некоторый барьер: для перехода от Python к традиционным C/C++ или даже языку Arduino придется заново выучивать ряд незнакомых приемов и понятий. По этой причине тем, кто собирается всерьез заняться программированием пусть даже только в сфере микроконтроллерных устройств, не стоит начинать учиться с Python: переход от традиционных строгих языков с привычными элементами структурирования к Python дается все же легче. Стоит потренироваться для начала хотя бы, например, на классическом Arduino, чтобы узнать, «как это делается». – Прим. перев.
Начало работы  49 накладку, Adafruit также предлагает изящную наклейку MicroPython (www. adafruit.com/products/3270). Я рекомендую приобрести одну из них и с гор­ достью продемонстрировать, когда вы закончите читать книгу. История MicroPython MicroPython1 был создан и поддерживается Дэмиеном П. Джорджем (Damien P. George), Полом Соколовским (Paul Sokolovsky) и другими участниками проекта. Он был разработан как упрощенная и эффективная версия языка Python 3 с возможностью установки на небольшом микроконтроллере. Поскольку Python является интерпретируемым языком и, следовательно, в целом медленнее, чем компилируемые языки, MicroPython был максимально оптимизирован, чтобы мог работать на микроконтроллерах, которые обычно сами по себе медленнее и имеют гораздо меньше памяти, чем типичный персональный компьютер. Компиляция против интерпретации Компилируемые языки (например, C/C++) используют программу, называемую компилятором, для преобразования исходного кода из удобочитаемой текстовой формы в двоичную исполняемую форму. Это преобразование включает в себя несколько шагов, но в целом мы берем исходный код и компилируем его в двоичную форму. Поскольку он теперь находится в двоичной форме, процессор может выполнять сгенерированные операторы напрямую, без каких-либо дополнительных шагов (такова принципиальная картина). С другой стороны, интерпретируемые языки не компилируются, а преобразуются в двоичную форму (или промежуточную двоичную форму) на лету с помощью программы, называемой интерпретатором. Python 3 предоставляет исполняемый файл Python, который является одновременно интерпретатором и консолью, позволяющей запускать код по мере его ввода. Программы Python выполняют по одной строке кода за раз, начиная с верхней части файла. Таким образом, скомпилированные языки работают быстрее, чем интерпретируемые, поскольку код подготовлен к выполнению и не требует промежуточного этапа обработки кода перед выполнением в реальном времени2. Другой аспект заключается в том, что платы микроконтроллеров, такие как Arduino, требуют этапа компиляции, который вы должны выполнить на своем компьютере и сначала загрузить на плату двоичный исполняемый файл. Напротив, поскольку интерпретатор MicroPython работает непосредственно на оборудовании, нам не нужен промежуточный этап подготовки кода; мы можем запустить интерпретируемый язык прямо на оборудовании! 1 2 Copyright 2014–2023, Damien P. George, Paul Sokolovsky и сотрудники. – Прим. авт. Кроме того, интерпретатор устроен намного проще компилятора, требования к строгости текста программы упрощаются, она легко переносится между различными платформами, облегчается и ускоряется ее разработка и т. д. – Прим. перев.
50  Знакомство с MicroPython Это позволяет производителям оборудования создавать небольшие недорогие платы, включающие MicroPython обычно на том же чипе, что и микропроцессор. Это дает вам возможность подключиться к плате, написать код и выполнить его без каких-либо дополнительных действий. Вы можете подумать, что для уменьшения Python 3 до размера, который поместится на небольшом чипе с ограниченной памятью, язык урезается и в нем отсутствуют какие-то функции. Это неправда. Фактически MicroPython представляет собой полную реализацию основных функций Python 3, включая компактную среду выполнения и интерактивный интерпретатор. Имеется поддержка чтения и записи файлов, загрузки модулей, взаимодействия с оборудованием, таким как выводы GPIO, обработки ошибок и многого другого. Самое приятное то, что оптимизация кода Python 3 позволяет скомпилировать его самого в двоичный файл, требующий около 256 Кбайт памяти для хранения и могущий работать всего с 16 Кбайт ОЗУ. Однако в языке Python 3 есть несколько вещей, которые MicroPython не реа­лизует. Следующие разделы дадут вам представление о том, что вы можете делать с помощью MicroPython, а что – нет. Возможности MicroPython Самая большая особенность MicroPython, конечно же, заключается в том, что он работает на Python. Это позволяет создавать простые, четко определенные и понятные программы. Я думаю, что уже в этом его лучшее преимущество перед другими платформами, такими как Arduino. Ниже перечислены некоторые функции, которые поддерживает MicroPython. В этой книге далее мы рассмотрим эти особенности более подробно.   Интерактивный интерпретатор: платы MicroPython имеют встроенную специальную интерактивную консоль, доступ к которой можно получить, подключившись к плате с помощью USB-кабеля (или, в некоторых случаях, через Wi-Fi). Эта консоль называется циклом чтения– оценки–печати (Read-Evaluate-Print Loop, REPL), который позволяет вам вводить код и выполнять его по одной строке за раз. Это замечательный способ создать прототип вашего кода или просто запускать проект по мере его разработки.   Стандартные библиотеки Python: MicroPython также поддерживает многие стандартные библиотеки Python. В целом можно ожидать, что MicroPython поддерживает более 80 % наиболее часто используемых библиотек. К ним относятся анализ JavaScript Object Notation (JSON)1, программирование сокетов, манипуляции со строками, ввод/вывод файлов и даже поддержка регулярных выражений.   Библиотеки аппаратного уровня: MicroPython имеет встроенные биб­ лиотеки, которые позволяют вам напрямую получать доступ к оборудо1 www.json.org. – Прим. авт.
Начало работы  51 ванию, включая или отключая выводы, считывая аналоговые и цифровые данные и даже управляя оборудованием с помощью широтно-импульсной модуляции (ШИМ) – способ ограничить мощность устройства, быст­ ро модулируя напряжение питания, например заставляя вентилятор вращаться медленнее, чем если бы он работал на полную мощность.   Расширяемость: MicroPython также является расширяемым. Это отличная функция для опытных пользователей, которым необходимо реализовать какую-то сложную библиотеку на низком уровне (на C или C++) и включить новую библиотеку в MicroPython. Да, это означает, что вы можете создать свой собственный уникальный код и сделать его частью набора функций MicroPython. Если попробовать ответить на возможный вопрос: «Что я могу сделать с MicroPython?», то ответов будет довольно много! Вы можете управлять оборудованием, подключенным к плате MicroPython, создавать новые модули кода для расширения возможностей программы, сохраняя их на SD-карте для последующего использования (точно так же, как можете это делать в Python на ПК), и многое другое. Аппаратное обеспечение, к которому вы можете подключиться, включает в себя включение и выключение светодиодов, управление сервоприводами, считывание данных с датчиков и даже отображение текста на ЖК-дисплеях. Некоторые платы также имеют поддержку сети в виде радиомодулей Wi-Fi. Практически все, что вы можете сделать с другими микроконтроллерными платами, вы можете сделать и с платой MicroPython. Однако есть несколько ограничений для запуска MicroPython на микроконтроллерных чипах. Ограничения MicroPython Самое большое ограничение MicroPython является следствием простоты его использования. Простота использования Python означает, что код интерпретируется «на лету». И хотя MicroPython очень оптимизирован, действия интерпретатора все равно отнимают время и ресурсы. Это означает, что проекты, требующие высокой степени точности в реальном времени, такие как высокоскоростная выборка данных или передача данных через соединение (USB, аппаратный интерфейс и т. д.), могут работать недостаточно быстро. В этих областях мы можем решить проблему, расширив язык MicroPython оптимизированными библиотеками для обработки низкоуровневого взаимодействия. MicroPython также использует несколько больше памяти, чем другие платформы микроконтроллеров, такие как Arduino. Обычно это не проблема, но вам следует учитывать это, если программа начинает разрастаться. Более крупные программы, использующие большое количество библиотек, могут потреблять больше памяти, чем вы ожидаете. Опять же, это связано с прос­ тотой использования Python – еще одна цена, которую придется заплатить.
52  Знакомство с MicroPython Наконец, как упоминалось ранее, MicroPython не реализует все функции всех библиотек Python 3. Однако вы обнаружите, что в нем есть все необходимое для создания проектов интернета вещей (и даже больше). Применимы ли мои навыки Python к MicroPython? Если вы уже учились программировать на Python, возможно, вы ожидаете увидеть в MicroPython что-то необычное или даже странное. Хорошая новость: ваши навыки Python – это все, что вам нужно для работы с MicroPython. Действительно, MicroPython и Python используют один и тот же синтаксис; нет ничего нового, чему можно было бы научиться. Как вы увидите в следующих нескольких главах, MicroPython реализует подмножество библиотек Python, но по-прежнему остается Python. На чем может быть запущен MicroPython? В связи с растущей популярностью MicroPython регулярно добавляются новые возможности по выбору плат для запуска MicroPython. Отчасти это заслуга разработчиков, создающих скомпилированные версии MicroPython для конкретных процессоров и платформ, которые вы можете загрузить и установить на плату. Это самая быстрорастущая категория. Существует две категории плат, которые можно использовать для запуска MicroPython. Во-первых, это платы с загруженным MicroPython еще на производстве, работающие только на MicroPython. К ним относятся Raspberry Pi Pico и Pico W, Arduino Nano RP2040 Connect, Pyboard (оригинальная плата MicroPython) и аналогичные платы, в которых используется чип RP2040 от Raspberry Pi. Во-вторых, имеются платы, для которых доступны варианты прошивки с установкой MicroPython, например ESP8266, Teensy и другие. Мы узнаем больше об этих платах в следующей главе. В следующем разделе мы изучим Python на нашем ПК, чтобы получить представление об этом языке и возможность опробовать его самостоятельно, не нуждаясь пока в плате MicroPython. Экспериментируем с Python на ПК Поскольку MicroPython – это Python (только немного уменьшенный в целях оптимизации), вы можете запустить Python на своем ПК и поэкспериментировать с языком. Я рекомендую загрузить Python на свой компьютер, даже если у вас уже есть плата MicroPython. Возможно, вам будет удобнее опробовать что-то на своем ПК, поскольку тут вы лучше сможете контролировать среду. Не забудьте, что ваш компьютер не сможет взаимодействовать с электронными компонентами или оборудованием, такими как платы MicroPython или внешние модули, поэтому хотя вы можете делать гораздо больше на ПК, вы не сможете протестировать код, который взаимодействует
Начало работы  53 с оборудованием. Но вы можете протестировать базовые конструкции, такие как вызовы функций, вывод сообщений и т. д. Проще говоря, использование ПК для отладки кода Python позволит вам отладить большую часть вашего проекта и начать работать, прежде чем пробовать его на плате MicroPython. Точнее, разрабатывая рутинные задачи на своем ПК, вы устраняете множество потенциальных проблем при отладке кода на плате MicroPython. Это ошибка номер один, которую допускают начинающие программисты: написание всего решения без тестирования отдельных мелких частей. Всегда лучше начинать с малого и тестировать небольшую часть кода за раз, добавляя только те части, которые были протестированы и показали корректную работу. Все, что вам нужно для начала, – это загрузить и установить Python 3 (например, Python 3.11 – последняя версия на момент написания этих строк, но периодически появляются новые версии). В следующих разделах кратко описывается, как установить Python на различных платформах. Конкретную информацию о платформах, не перечисленных здесь, см. в Python-вики по адресу https://wiki.python.org/moin/BeginnersGuide/Download. ВНИМАНИЕ! Доступны две версии Python – Python 2 и Python 3. Поскольку MicroPython основан на Python 3, вам потребуется установить Python версии 3, а не Python версии 2. Но сначала проверьте свою систему и убедитесь, что Python уже установлен. Откройте окно терминала (командную строку) и введите следующую команду: python --version Если Python установлен, вы должны получить что-то вроде следующего: $ python --version Python 3.11.4 Если вы увидели такую версию, как Python 2.x.y, все еще есть вероятность, что на вашем компьютере имеется Python 3. В некоторых системах установлен как Python 2, так и Python 3. Чтобы запустить Python 3, используйте следующую команду: python3 Если Python 3 не установлен или это старая версия, изучите следующие разделы для установки Python в вашей системе. Вы можете загрузить последний Python 3 по адресу www.python.org/downloads/. Установка Python 3 в Windows 11 Большинство компьютеров с Windows не имеют Python, и вам необходимо его установить. Вы можете загрузить Python 3 для Windows с официального сайта Python (www.python.org/downloads/windows/). Вы найдете обычные
54  Знакомство с MicroPython варианты установщика Windows для 32-битных и 64-битных версий, а также веб-установщик и формат .zip. Большинство людей будут использовать вариант установщика Windows, но если вам необходимо установить Python вручную, вы можете использовать другие варианты. Загрузив Python, вы можете запустить установщик. Например, на моем компьютере с Windows 11 я загрузил файл по ссылке Latest Python 3 Release ⇒ Python 3.11.4 (Последняя версия Python 3 ⇒ Python 3.11.4). Если вы прокрутите вниз, то сможете найти нужный вам установщик. Я выбрал установщик для 64-битных компьютеров Windows. При этом был загружен файл с именем python-3.11.4-amd64.exe, который я нашел в папке Downloads (Загрузки) и запустил, дважды щелкнув по нему. Совет. Обязательно установите флажок для добавления исполняемого файла Python в командную строку, чтобы можно было запускать python.exe без указания пути. Как и при большинстве установок в Windows, вам придется пройти через ряд диалоговых окон, согласившись с лицензией, указав, где вы хотите установить программу, и, наконец, начать установку. На рис. 2.2 показан пример работающей программы установки. Рис. 2.2  Установка Python в Windows 11 СОВЕТ Если установка застопорилась или вам потребовались более подробные инструкции, прочтите отличную статью на сайте «How-To Geek»: www.howtogeek.com/ 197947/how-to-install-python-on-windows/. После завершения установки вы можете попробовать выполнить тест, описанный в предыдущем разделе, чтобы проверить установку. Если вы не
Начало работы  55 измените переменную PATH, вам может потребоваться использовать ярлык консоли Python в меню Run (Пуск) для запуска консоли. Установка Python 3 на macOS Если вы используете macOS, у вас, вероятно, установлен Python, поскольку в большинстве выпусков macOS Python устанавливается по умолчанию. Однако если вам не удалось запустить показанную ранее команду версии Python 3 или это была неправильная версия, вы все равно можете загрузить последнюю версию Python 3 с веб-сайта Python (www.python.org/downloads/ mac-osx/). Вы найдете несколько версий, но вам следует загрузить последнюю доступную версию. Загрузив Python, вы можете запустить установщик. Например, на моем MacBook Pro на базе M1 я загрузил последнюю 64-битную версию файла Python 3 по ссылке macOS 64-bit universal2 installer. Если вы прокрутите вниз, то сможете найти нужный вам установщик. При этом был загружен файл с именем python-3.11.4-macosx11.pkg, который я нашел в папке Downloads (Загрузки). Как и для большинства установщиков, вы можете пройти через различные диалоговые окна, согласившись с лицензией, указав, где вы хотите установить программу, и, наконец, начать установку. На рис. 2.3 показан пример работающей программы установки. Рис. 2.3  Установка Python на macOS
56  Знакомство с MicroPython ПРИМЕЧАНИЕ В зависимости от того, какую версию macOS вы используете и как установлены параметры безопасности, вам может потребоваться изменить их для запуска установщика, поскольку разработчик не подписывает его. См. панель Security & Privacy (Безопасность и конфиденциальность) в System Preferences (Системные настройки). После завершения установки для проверки вы можете попробовать выполнить тест, описанный в начале раздела «Экспериментируем с Python на ПК». Установка Python 3 в Linux Если вы используете Linux, способ установки Python будет зависеть от платформы. Например, Ubuntu, Debian, Raspbian и т. д. используют команды aptget, тогда как в других дистрибутивах есть иные менеджеры пакетов. Используйте менеджер пакетов по умолчанию для вашей платформы для установки Python 3.11 (или более поздней версии). Например, в Debian или Ubuntu мы устанавливаем пакет Python 3.6, используя следующие команды. Первая команда обновляет пакеты, чтобы гарантировать наличие последних версий. Вторая команда инициирует загрузку необходимых файлов и устанавливает Python. sudo apt-get update sudo apt-get install python3.11 Если вы используете Raspberry Pi, скорее всего, у вас установлена операционная система Raspberry Pi (также называемая Raspbian). Базовый образ включает Python 3.9, и этого достаточно для проектов, описанных в данной книге. Однако если вы хотите установить Python 3.11 или более позднюю версию, то можете следовать инструкциям на странице https://raspberrytips. com/install-latest-python-raspberry-pi/. После завершения установки вы можете попробовать выполнить тест, описанный в начале раздела «Экспериментируем с Python на ПК», чтобы проверить установку. Запуск консоли Python Теперь давайте проведем несколько тестов на нашем ПК. Напомним, мы можем открыть консоль Python, открыв окно терминала (командную строку) и введя команду python (или python3 или python3.11 в зависимости от установки). Как только вы увидите приглашение (>>>), введите следующий код. Этот код выведет сообщение на экран. >>> print ("Hello, World!") После ввода такого кода вы сразу увидите результат. Напомним, что интерпретатор работает, выполняя по одной строке кода за раз, когда вы нажимае­ те <Enter>. Однако, в отличие от запуска программы, хранящейся в файле,
Начало работы  57 код, который вы вводите в консоль, не сохраняется. В лис­тин­ге 2.1 показан пример запуска простой программы Python с помощью консоли Python. Обратите внимание, что я набрал простую программу – типичный вывод фразы «Hello, World!» для примера. Листинг 2.1  Hello, World! (консоль Python) C:\Users\chuck> python Python 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> print("Hello, World!") Hello, World! >>> quit() C:\Users\chuck> Чтобы выйти из консоли, введите код quit(), как показано в лис­тин­ге 2.1. Хотя это и демонстрирует запуск Python с вашего ПК, это не так уж интересно. Давайте посмотрим на что-то немного более сложное. Запуск программ Python с помощью интерпретатора Предположим, ваш проект требует сохранения данных в файл или, возможно, чтения данных из файла. Вместо того чтобы пытаться выяснить, как это сделать на плате MicroPython, мы можем поэкспериментировать с файлами на нашем ПК! В следующем примере я записываю данные в файл, затем считываю их и вывожу на экран. Не беспокойтесь слишком сильно о понимании кода, прос­то прочитайте его – он очень интуитивно понятен. В лис­тин­ге 2.2 показан код этого примера. Я использовал текстовый редактор и сохранил файл как file_io.py. Листинг 2.2  Пример файлового ввода-вывода # Example code to demonstrate writing and reading data to/from files # Step 1: Create a file and write some data new_file = open("log.txt", "w") # use "write" mode new_file.write("1,apples,2.5\n") # write some data new_file.write("2,oranges,1\n") # write some data new_file.write("3,peaches,3\n") # write some data new_file.write("4,grapes,21\n") # write some data new_file.close() # close the file # Step 2: Open a file and read data old_file = open("log.txt", "r") # use "read" mode # Use a loop to read all rows in the file for row in old_file.readlines(): columns = row.strip("\n").split(",") # split row by commas print(" : ".join(columns)) # print the row with colon separator old_file.close()
58  Знакомство с MicroPython Конструкция \n в конце строк – это специальный непечатаемый символ, который вызывает возврат каретки для перехода на новую строку (аналогично нажатию <Enter> при обычном вводе). Я сохранил код в файл, чтобы показать вам, как можно выполнять сценарии Python с помощью интерпретатора Python, используя команду запуска файла: python ./file_io.py Если вы используете Windows, вам может потребоваться изменить команду, чтобы использовать правильную версию Python (например, на python3. exe file_io.py). В лис­тин­ге 2.3 показаны результаты выполнения сценария. Листинг 2.3  Результат выполнения примера файлового ввода-вывода C:\Users\chuck\Source\Ch02> python ./file_io.py 1 : apples : 2.5 2 : oranges : 1 3 : peaches : 3 4 : grapes : 21 C:\Users\chuck\Source\Ch02> Обратите внимание, что код меняет разделитель данных, заменяя запятую, записанную в файле изначально, на пробел, двоеточие и еще один пробел. Код распознает отдельные данные, отделенные запятыми в прочитанной строке. Следовательно, данные строки состоят из трех частей, и мы используем метод join(), чтобы снова соединить строку и вывести ее на экран. Найдите минутку, чтобы внимательно прочитать код, и вы увидите эти нюансы. Как видите, Python легко читается. Теперь, когда мы поэкспериментировали с Python на ПК, давайте посмот­ рим, как использовать MicroPython на типичной плате. Как работает MicroPython Напомним, что MicroPython предназначен для работы на небольших микроконтроллерных платформах. Некоторые из этих платформ используют специальный чип, который содержит двоичные файлы MicroPython (библиотеки, базовый дисковый ввод-вывод, загрузку и т. д.), а также микроконтроллер, память и вспомогательные компоненты. Когда вы используете плату MicroPython (как и большинство обычных микроконтроллеров), вы должны сначала написать свой код и загрузить его на плату. Большинство плат MicroPython имеют встроенный USB-накопитель, который доступен при подключении к компьютеру с помощью USB-кабеля. На этом флеш-накопителе хранится несколько файлов, которые вы можете
Как работает MicroPython  59 изменить, чтобы изменить поведение этого накопителя. Вы также можете скопировать свою программу (файл сценария) на этот диск для выполнения во время загрузки. Мы увидим, как это сделать, в следующей главе. Вы также можете использовать консоль MicroPython, которая очень похожа на консоль Python, которую мы видели в предыдущем разделе. Как мы говорили, консоль MicroPython называется Read-Evaluate-Print Loop (REPL). Консоль упрощает начало работы и отладку (удаление ошибок) вашей программы перед ее загрузкой на плату для выполнения при каждом включении. Консоль REPL Если вы использовали другие платы микроконтроллеров, например Arduino, вы, вероятно, знакомы с некоторыми из следующих элементов. Но если вы незнакомы с ними или не использовали терминальную программу, я предоставляю все необходимые вам шаги для каждой из трех основных платформ: Windows, macOS и Linux. В следующих разделах описывается первое подключение к плате. Для этих примеров я использую Raspberry Pi Pico. Вы можете использовать любую другую плату, на которой по умолчанию загружен MicroPython, но обязательно ознакомьтесь с документацией поставщика, прежде чем начинать применять плату в первый раз. Некоторые платы могут потребовать загрузки прошивки перед первым использованием. Подключение платы Чтобы начать использовать консоль REPL, подключите плату к компьютеру с помощью кабеля USB или micro-USB (обычно), который обеспечивает питание, а также подключение консоли. После загрузки платы (первая загрузка некоторых плат может занять от одной до двух минут) вы можете подключиться к плате с помощью терминальной программы. После этого мы можем использовать консоль REPL так же, как использовали консоль Python. СОВЕТ Загрузка некоторых плат может занять несколько минут, поэтому если соединение не установлено, подождите несколько минут и повторите попытку. Когда плата завершит загрузку, вы сможете просмотреть диск на плате с помощью компьютера. Лучший способ работать с платами MicroPython – использовать интерактивную среду разработки, такую как Thonny, которую мы рассмотрим более подробно в следующей главе. Используя Thonny, вы можете применять функцию проводника для просмотра файлов на доске. На рис. 2.4 показан пример файлов на Raspberry Pi Pico, которые я буду использовать в этой главе.
60  Знакомство с MicroPython Рис. 2.4  USB-накопитель (Raspberry Pi Pico) Обратите внимание, что имеется один файл с расширением .py, boot.py, который выполняется при загрузке (отсюда и название). Вы также можете создать файл с именем main.py, который можно использовать для хранения вашей собственной программы, если вы хотите использовать плату независимо (основная программа запускается при загрузке). Опять же, мы увидим это в действии в последующих главах. Запуск консоли REPL (Windows) Если вы не хотите использовать Thonny, то можете подключиться к консоли REPL. Например, при использовании Windows вам понадобится терминальная программа, такая как PuTTY, разработанная Саймоном Тэтэмом (Simon Tatham, www.chiark.greenend.org.uk/~sgtatham/putty/latest.html). PuTTY – это простая терминальная программа, очень удобная в использовании и созданная для платформы Windows. Чтобы установить PuTTY, по указанной ссылке выберите соответствующий файл .msi (32- или 64-разрядный), загрузите его, затем дважды щелкните файл, чтобы запустить установщик. Следуйте инструкциям для завершения установки. Например, я скачал файл с именем putty-64bit-0.68-installer.msi. Теперь, когда у вас установлен PuTTY, вы должны знать правильный порт для использования. Откройте Device Manager (Диспетчер устройств) и перемещайтесь вниз по дереву, пока не найдете пункт Ports (COM & LPT) (Порты COM и LPT), и щелкните его, чтобы открыть. Обратите внимание, что до этого плата должна быть подключена. В указанном пункте вы должны увидеть одну или несколько записей, обозначающих COM-порты (а также,
Как работает MicroPython  61 возможно, порты принтера), подключенные к вашему ПК. Плата MicroPython будет указана просто как USB Serial Device (COMn), где COMn – это номер типа COM1, COM2 и т. д. Например, на моем компьютере она была указана как COM3. На рис. 2.5 показан пример моего ПК. Рис. 2.5  Поиск COM-порта в Windows 11 Теперь мы можем открыть PuTTY и подключиться к плате. Используйте ярлык в меню Start (Пуск), чтобы запустить PuTTY, или введите PuTTY в поле поиска и щелкните запись, когда она найдется. Когда PuTTY откроется, вы увидите диалоговое окно, которое можно использовать для подключения через терминал по сети. Поскольку плата подключена через COM-порт, мы должны нажать на небольшой переключатель с надписью Serial. Затем мы можем ввести COM-порт в текстовое поле Serial line и установить скорость 9600. На рис. 2.6 показан правильно настроенный PuTTY для подключения к плате через COM3 (найденный в Диспетчере устройств). Когда будете готовы, нажмите кнопку Open (Открыть). Подождите, пока консоль REPL ответит. Если она не отображает приглашение >>>; попробуйте нажать <Enter>. Откроется новый терминал, который запустит консоль REPL, как показано на рис. 2.7. Я ввел простой оператор, чтобы продемонстрировать, что консоль работает. СОВЕТ Если вам не нравится цветовая схема «белый (серый) на черном», вы можете изменить ее, щелкнув Colours (Цвета) в дереве элементов управления. Действуйте осторожно, поскольку значения необходимо устанавливать отдельно (схемы концепции нет).
62  Знакомство с MicroPython Рис. 2.6  Подключение к плате MicroPython с помощью PuTTY Рис. 2.7  Консоль REPL (Windows 11)
Запуск MicroPython  63 Запуск консоли REPL (macOS и Linux) Чтобы подключиться с помощью macOS или Linux, вы можете использовать команды, приведенные ниже. Единственное, что вам может потребоваться сделать, – это найти устройство. Я демонстрирую этот шаг с помощью первой команды, которая выводит список устройств в моей системе macOS. Сделайте это после подключения к плате MicroPython. $ ls /dev/tty.usb* /dev/tty.usbmodem1422 $ screen /dev/tty.usbmodem1422 С консолью REPL есть одна странность. Функция quit() иногда не работает. Чтобы выйти из консоли некоторых плат, вам потребуется перезагрузить плату или разорвать соединение. Хотя это кажется странным, я уверен, что в будущем это будет исправлено, и напоминаю, что мы обычно не используем консоль REPL для запуска наших проектов; скорее, мы используем ее для тестирования кода, поэтому небольшая заминка при выходе не является большой проблемой. ВНИМАНИЕ! Вам следует воздерживаться от простого отключения платы MicroPython. Некоторые платы, такие как Pyboard, представляют свою базовую файловую систему в виде подключаемого USB-накопителя. Физическое отключение без извлечения платы может привести к потере данных. Теперь пришло время познакомиться с тем, что мы можем сделать с помощью MicroPython. В следующем разделе используется несколько примеров проектов, чтобы продемонстрировать, что можно делать с платой MicroPython. Я еще раз представлю примеры с минимальными пояснениями и подробностями об оборудовании. В следующей главе мы узнаем больше об аппаратном обеспечении. Запуск MicroPython Если вы, как и я, сталкиваетесь с инновационными технологиями, то, скорее всего, захотите начать работу как можно скорее. Если у вас уже есть Raspberry Pi Pico или Pico W, вы можете следовать примерам в этом разделе и увидеть еще несколько примеров того, что вы можете сделать с MicroPython. В примерах используются некоторые основные электронные компоненты, такие как резисторы, светодиоды, тактовая кнопка и перемычки. Если у вас есть эти компоненты и вы хотите воспроизвести примеры, смело делайте это. Не волнуйтесь, если вам это покажется незнакомым. В последующих главах
64  Знакомство с MicroPython мы рассмотрим каждый из этих компонентов, в том числе способы использования макетной платы для подключения компонентов. На рис. 2.8 показана схема соединения компонентов, поддерживающая все три примера. Рис. 2.8  Схема подключения компонентов для примеров Обратите внимание, что у нас есть Raspberry Pi Pico, установленный на макетной плате, четыре разноцветных светодиода, каждый из которых имеет одну ножку (отрицательный вывод или катод), соединенную с общим проводом1, подключенным с помощью перемычки к одному из общих контактов (GND) на Pico. Положительный вывод (или анод) каждого светодиода подключен к резистору сопротивлением 560 Ом, который подключен к разъему GPIO на Pico (контакты GPIO 14, 15, 16 и 17). Наконец, у нас есть тактовая кнопка, также подключенная к выводам Pico: одна сторона подключена к 3,3 В на Pico, а другая – к GPIO 13. Однако не волнуйтесь, если у вас еще нет макетной платы. Опять же, я включил этот раздел, чтобы помочь вам узнать больше о возможном на примерах. Обсудив детали популярных плат и способы программирования на MicroPython, мы углубимся в более сложные проекты, с которыми вы сможете экспериментировать по мере обучения. 1 Автор называет общий (отрицательный) провод питания схемы и соответствующие выводы компонентов словом ground, т. е. «заземляющими». Называть «общий провод» «заземлением» безграмотно и может привести к фатальным последствиям для схемы в случае возникновения путаницы с настоящей электротехнической землей, с которой часто соединены корпуса стационарных компьютеров, измерительных приборов или источников питания от сети. Сохранив общепринятое обозначение GND, мы в дальнейшем везде заменяем авторский термин ground на «общий» или «общий провод». – Прим. перев.
Запуск MicroPython  65 Дополнительное оборудование Прежде чем углубиться в примеры, я хотел бы представить несколько ключевых компонентов, которые вам понадобятся далее в этой книге. Если у вас нет этих компонентов, разумно заказать их сейчас, чтобы, когда вы доберетесь до примеров проектов, у вас под рукой было то, что нужно. Помимо собственно платы MicroPython, эти компоненты я считаю обязательными для всех, кто хочет научиться работать с электроникой и Mic­ roPython. К ним относятся базовый комплект, содержащий наиболее распространенные компоненты, которые могут понадобиться при изучении электроники, макетная плата и перемычки. Все перечисленное описано в следующих разделах. Базовый комплект электроники В примерах проектов в этой книге используется несколько распространенных электронных компонентов, таких как светодиоды, переключатели, кнопки, резисторы и т. д. Одна из самых больших проблем при обучении работе с электроникой на уровне хобби – это то, что именно приобретать. Я разговаривал с некоторыми людьми, неоднократно посещавшими местный магазин электроники, чтобы купить то, что им нужно, но, похоже, у них никогда не было под рукой подходящих компонентов, что бы они ни покупали. К счастью, розничные продавцы электроники уловили эту проблему и теперь предлагают базовый комплект электроники, который содержит множество наиболее распространенных компонентов. Такие наборы предлагают как Adafruit, так и SparkFun. Хотя вы не ошибетесь, выбрав любой из них, мне больше всего нравится комплект Adafruit, поскольку в нем больше компонентов (например, больше светодиодов)1. Adafruit Parts Pal поставляется в небольшом пластиковом корпусе со множеством электронных компонентов. На рис. 2.9 показан комплект Parts Pal. 1 Нет необходимости искать в отечественной продаже сравнительно дорогие и трудно доставаемые готовые наборы, тем более от малодоступных ныне американских фирм Adafruit и SparkFun. Далее приводится перечень компонентов в наборе Adafruit Parts Pal, который здесь несколько дополнен и конкретизирован. Все перечисленные компоненты можно приобрести в отечественных интернет-магазинах. Перечислим самые популярные из таких торговых точек (велика вероятность, что по этим адресам вы сможете прибрести большую часть необходимого в одном месте, включая и платы микроконтроллеров, описываемые далее): «Чип и дип» (http:// www.chipdip.ru); «Амперка» (https://amperka.ru); iArduino (https://iarduino.ru); «Dip8» (https://dip8.ru), «Электронщик» (https://www.electronshik.ru) и др. Причем при желании вы можете также приобрести специальный ящик с отделениями для удобства хранения компонентов, получив набор, ничем не отличающийся от Adafruit, но существенно дешевле и с меньшими хлопотами. Также заметим, что на AliExpress можно приобрести все указанное максимально дешево (хотя и с потерей времени), но идти по этому пути без опыта не следует: сэкономив не так уж много, вы рискуете получить что-то неработоспособное вовсе или работающее с большими оговорками. – Прим. перев.
66  Знакомство с MicroPython Рис. 2.9  Adafruit Parts Pal (с разрешения adafruit.com) В комплект входят следующие компоненты: инструменты для прототипирования, светодиоды, конденсаторы, резисторы, некоторые базовые датчики и многое другое. На самом деле компонентов в этом наборе больше, чем вам понадобится для многих экспериментов. Более того, комплект стоит всего 19,95 доллара, что делает его выгодной сделкой (а ящик – отличным бонусом).                   1 Ящик для хранения с защелкой – 1 шт. Макетная плата половинного размера1 – 1 шт. Перемычки «штырь/штырь» 3 дюйма (75 мм) – 20 шт. Перемычки «штырь/штырь» 6 дюймов (150 мм) – 10 шт. Перемычки «штырь/гнездо» 3 дюйма (75 мм) – 10 шт. Перемычки «гнездо/гнездо» 3 дюйма (75 мм) – 10 шт. Зеленые светодиоды в рассеивающем корпусе 5 мм – 5 шт. Красные светодиоды в рассеивающем корпусе 5 мм – 5 шт. Желтые светодиоды в рассеивающем корпусе 5 мм – 5 шт. Синие светодиоды в рассеивающем корпусе 5 мм – 5 шт. RGB-светодиод с общим анодом 10 мм – 1 шт. Конденсаторы керамические 1,0 мкФ – 10 шт. Конденсаторы керамические 0,1 мкФ – 10 шт. Конденсаторы керамические 0,01 мкФ – 10 шт. Конденсаторы электролитические 10 мкФ 50 В – 5 шт. Конденсаторы электролитические 10 мкФ 16 В – 5 шт. Резисторы с осевыми выводами 560 Ом 5 % – 10 шт. Резисторы с осевыми выводами 1 кОм 5 % – 10 шт. См. раздел «Макетная плата и перемычки» далее. – Прим. перев.
Запуск MicroPython  67                    Резисторы с осевыми выводами 10 кОм 5 % – 10 шт. Резисторы с осевыми выводами 47 кОм 5 % – 10 шт. Диоды 1N4001 – 5 шт. Импульсные диоды 1N4148 – 5 шт. NPN-транзистор PN2222 (корпус TO-92) – 5 шт. PNP-транзистор PN2907 (корпус TO-92 – 5 шт. Линейный стабилизатор напряжения LM7805 5В 1,5А (корпус ТО-220) – 2 шт. Линейный стабилизатор напряжения LD1117 3,3 В 800 мА (корпус TO-220) – 1 шт. Таймер TLC555 – 1 шт. Фоторезистор GL5516 – 1 шт. Термистор B57164-K 103-J, 10 кОм – 1 шт. Датчик вибрации (например, SW-420) – 1 шт. Резистор переменный или подстроечный 10K – 1 шт. Резистор переменный или подстроечный 10K – 1 шт. Пьезозуммер – 1 шт. Тактовые кнопки – 5 шт. Ползунковые переключатели SPDT1 – 3 шт. Вилка 40-контактного разъема, шаг 2,54 мм (PLS-40) – 1 шт. Гнездо 40-контактного разъема, шаг 2,54 мм (PBS-40) – 1 шт. Макетная плата и перемычки Макетная плата – это специальный инструмент, предназначенный для подключения электрических компонентов при обеспечении контактов в столбцах, чтобы вы могли подключить выводы двух компонентов к одному и тому же столбцу и, следовательно, выполнить соединение. Плата разделена на два ряда столбцов, что позволяет легко использовать микросхему в центре платы. Проводники (называемые проводными перемычками или просто перемычками) можно использовать для подключения схемы на макетной плате к плате MicroPython и другим компонентам или модулям. Вы увидите пример позже в этой главе. К счастью, в комплект поставки Adafruit Parts Pal входит макетная плата, подобная показанной на рис. 2.10. На этом изображении плата половинного размера от Adafruit. Эта плата называется половинной, поскольку ее длина составляет половину нормальной длины стандартной макетной платы2. 1 2 SPDT – Single Pole Double Throw (один полюс, два направления) означает тип переключателя с одним общим контактом, который может замыкаться с одним или другим из двух остальных. – Прим. перев. Полноразмерной считается макетная плата длиной 175–180 мм и шириной 55– 68 мм, содержащая около 800 контактных гнезд в 60+ столбцах. Плата половинного размера урезана вполовину по длине (80–85 мм) и содержит около 400 контактов в 30+ столбцах. Для удобства макетирования предпочтительно использовать полноразмерную плату. Не приобретайте макетные платы у случайных продавцов, перепродающих продукцию с Ali Express, – они быстро выходят из строя! – Прим. перев.
68  Знакомство с MicroPython Рис. 2.10  Макетная плата половинного размера (с разрешения adafruit.com) Если у вас уже есть некоторые компоненты или вы решили купить другой базовый комплект электроники, в комплект которого не входит макетная плата, вы можете приобрести ее отдельно. Если на вашей плате MicroPython разъемы типа «штырь» вместо разъемов «гнездо», вам понадобится отдельный набор перемычек. Вам для разных модулей могут понабиться перемычки «штырь/гнездо» или даже «гнездо/ гнездо», и их лучше приобрести заранее. На рис. 2.11 показан набор перемычек типа «штырь/гнездо». Рис. 2.11  Перемычки «штырь/гнездо» (с разрешения adafruit.com) Теперь давайте посмотрим на оборудование в действии!
Запуск MicroPython  69 Пример 1. Мигание светодиода В этом примере мы продемонстрируем, как написать код для включения одного из светодиодов. Ничего страшного, если у вас еще не готова макетная плата (мы приступим к ней в главе 3), но если она все же у вас уже готова, вы можете следовать инструкциям. Опять же, это сделано в демонстрационных целях, чтобы вы могли увидеть, насколько просто можно работать с железом. Если вы расположите плату контроллера с добавленными компонентами, как показано на рис. 2.8, четыре светодиода будут иметь разные цвета, расположенные слева направо: красный, зеленый, синий и желтый. Если вы правша, как я, вы, вероятно, ориентируете плату так, чтобы разъем USB находился слева. В этом случае красный светодиод окажется крайним слева. Давайте напишем код для включения красного светодиода. Вместо того чтобы просто включать его, воспользуемся конструкцией, называемой цик­ лом, для его включения и выключения каждые 250 миллисекунд. Таким образом, он будет мигать довольно быстро – два раза в секунду. Прежде чем я объясню, как это делается, давайте посмотрим на готовый код, показанный в лис­тин­ге 2.4. Не волнуйтесь; далее я объясню каждую строку. Листинг 2.4  Пример 1: мигание светодиодом # # MicroPython for the IoT Second Edition # # Example 1 – Blink LED # # Dr. Charles Bell # from machine import Pin # Get the machine Pin class from utime import sleep # Import the sleep method led = Pin(14, Pin.OUT) # Get the GPIO 14 pin as output led.off() # Make sure it's off first for i in range(0, 40): # Run the indented code 40 times led.on() # Turn LED on sleep(0.25) # Wait for 250 milliseconds led.off() # Turn LED off sleep(0.25) # Wait for 250 milliseconds led.off() print("Done!") # Turn the LED off at the end # Goodbye! Первые строки кода – это комментарии. Они игнорируются MicroPython и являются способом сообщить другим программистам, что делает ваша программа. Не стесняйтесь пропускать строки комментариев при вводе кода в консоль REPL. Далее следует строка кода, которая используется для импорта класса Pin из библиотеки machine с последующим импортом метода Sleep() из библиотеки
70  Знакомство с MicroPython utime. Следующие две строки кода инициализируют переменную (led), подключенную к выводу GPIO 14, настраиваемому как выход (Pin.OUT), затем мы немедленно выключаем светодиод, вызывая функцию led.off(). Далее идет основная часть кода – цикл. В данном случае это цикл вида for, предназначенный для запуска блока кода ниже, отделенного отступами, 40 раз подряд. Цикл for использует счетчик i, принимающий значения от 0 до 39, возвращаемые функцией range(0, 40) (в функции range последнее выполняемое значение на единицу меньше, чем заданное значение верхнего предела). В теле цикла (часть с отступами) мы сначала включаем светодиод с помощью функции led.on(), ждем 250 миллисекунд с помощью метода sleep(), принимающего интервал времени, выраженный в секундах (следовательно, число 0,25 эквивалентно 250 миллисекундам), затем снова выключаем светодиод и ждем еще 250 миллисекунд до возврата к началу цикла. Наконец, по прошествии 40 включений-выключений светодиод выключается окончательно, и выводится сообщение о том, что все сделано ("Done!"). Вы можете ввести этот код построчно в консоль REPL. Можете пропустить те строки, которые начинаются с #. После ввода оператора цикла for вам будет предоставлена строка без приглашения >>>. Это нормально. Введите следующие строки, используя пару пробелов1 (для отступа, который устанавливает блок тела цикла). После второго вызова sleep() нажмите <Enter> на пустой строке, чтобы завершить блок c отступами. Взгляните на макетную плату сейчас. Должен мигать красный светодиод. Помните, что консоль REPL, как и консоль Python, запускает код по мере его ввода. Если вы хотите, чтобы светодиод мигал медленнее, просто измените параметр вызова sleep(), увеличив значение до 0,50 или даже 1,0. Когда код закончится, вы должны увидеть сообщение об окончании работы следующим образом: Done! >>> ПРИМЕЧАНИЕ На некоторых платформах, таких как macOS и Linux, вы можете использовать текстовый редактор для ввода кода, а затем скопировать его и вставить в консоль REPL. Однако это работает не для всех платформ и терминальных программ. Попробуйте самостоятельно. Пример 2. Последовательное мигание светодиодов Теперь давайте последовательно включать и выключать светодиоды, используя переменную-счетчик внутри другого цикла другой разновидности (цикла 1 Стандартно в нотации Python пробелов для единичного отступа полагается четыре, но это не имеет принципиального значения: обычно ставят от двух до четырех, главное, чтобы для каждого выделенного блока текста программы во всех строках отступ был одинаковым (в Python выделение отступами заменяет операторные скобки, такие как {} в языках с С-подобным синтаксисом или begin … end в Pascal и Delphi, подробнее см. главу 4). – Прим. перев.
Запуск MicroPython  71 типа while). В этом случае мы должны переключать светодиоды по одному, включая каждый из них, а затем выключая его после небольшой задержки. Это похоже на предыдущий пример, но немного сложнее. Пусть, например, мы храним экземпляры переменных, обозначающих светодиоды (из класса Pin) в массиве leds. В этом примере используются два цикла. Сначала мы перебираем свето­ диоды, выключая их все. Ниже показано, как это сделать. Обратите внимание: я использую переменную-счетчик i в диапазоне от нуля до трех (напоминаем, верхний предел на единицу меньше, чем заданный в функции range). Внутри тела цикла я получаю светодиод из массива, используя этот счетчик, а затем выключаю его с помощью led.off(). Выключение светодиодов в начале – это хорошая привычка, которая позаботится о любых событиях, когда вы оставляете код работающим или прерываете его выполнение, оставляя один или несколько светодиодов включенными. Вы также можете вернуться к началу, перезагрузив плату, но принудительно вернуть все в исходное состояние будет более правильно (и является хорошей практикой). for i in range(0,4): # Turn off all of the LEDs leds[i].off() # Turn the LED off Далее мы используем еще один цикл, который будет выполняться десять раз. Внутри тела цикла я делаю нечто похожее на предыдущий пример: извлекаю светодиод из массива, включаю его, жду 250 миллисекунд, выключаю и жду еще 250 миллисекунд до возврата к началу цикла. В следующем коде показаны эти действия: for i in range(0, 10): for j in range(0,4): leds[j].on() sleep(0.25) leds[j].off() sleep(0.25) # # # # # # Run the indented code 10 times For each LED... Turn LED on Sleep Turn LED off Sleep Листинг 2.5  Пример 2: последовательное мигание светодиодов # # MicroPython for the IoT Second Edition # # Example 2 - Blink LEDs # # Dr. Charles Bell # from machine import Pin # Get the machine Pin class from utime import sleep # Import the sleep method # Create an array for the LEDs leds = [Pin(14, Pin.OUT), Pin(15, Pin.OUT), Pin(16, Pin.OUT), Pin(17, Pin.OUT)] for i in range(0,4): # Turn off all of the LEDs
72  Знакомство с MicroPython leds[i].off() # Turn the LED off for i in range(0, 10): for j in range(0,4): leds[j].on() sleep(0.25) leds[j].off() sleep(0.25) print("Done!") # # # # # # # Run the indented code 10 times For each LED... Turn LED on Sleep Turn LED off Sleep Goodbye! Запустив код, вы увидите, что светодиоды загораются последовательно, начиная с красного, затем зеленый, синий, желтый и опять красный (и таких 10 серий). Потратьте некоторое время на изучение кода и, если вы раньше программировали на Python, рассмотрите возможность превращения первого цикла (с выключением всех светодиодов) в метод и вызовите его в конце, чтобы убедиться, что все светодиоды выключены. Пример 3. Использование кнопки Этот пример демонстрирует, как использовать кнопку на макетной плате. Он также демонстрирует, как применять прерывание (называемое еще функцией обратного вызова). То есть мы создаем функцию в нашем коде, а затем говорим MicroPython выполнить эту функцию при возникновении прерывания – в данном случае при нажатии кнопки. Поскольку этот пример также более сложен, пробежимся по коду. Сначала мы импортируем аппаратные библиотеки и методы, которые хотим использовать, устанавливаем переменную для зеленого светодиода и выключаем его. Также создаем экземпляр класса кнопки из библиотеки Pin: from machine import Pin # Get the machine Pin class from utime import sleep # Import the sleep method # Green LED led = Pin(15, Pin.OUT) # Button button = Pin(13, Pin.IN, Pin.PULL_DOWN) led.off() # Turn off LED Далее определяем функцию, которая должна выполняться при нажатии кнопки. Это довольно легко: указываем директиву def и даем функции имя, в данном случае flash_led. Прерывание передаст функции (методу) переменную, но она ей не понадобится (потому она с именем not_used). Внутри этой функции мы быстро мигаем светодиодом (задержка 100 миллисекунд) пять раз. Код, позволяющий выполнить подобное, мы уже видели в предыдущих примерах: def flash_led(not_used): for i in range(0, 5):
Запуск MicroPython  73 led.on() sleep(0.1) led.off() sleep(0.1) Далее мы создаем еще одну переменную, определяя пользовательскую кнопку. Затем мы используем метод irq() и при вызове функции передаем условие возникновения прерывания (переменная trigger) и имя созданной нами функции (flash_led). Это установит порядок запуска функции flash_ led() при нажатии кнопки: # Register the callback (ISR) button.irq(trigger=Pin.IRQ_FALLING, handler=flash_led) В лис­тин­ге 2.6 показан готовый код полностью. Листинг 2.6  Пример 3: использование кнопки # # MicroPython for the IoT Second Edition # # Example 3 - Button with interrupt # # Dr. Charles Bell # from machine import Pin # Get the machine Pin class from utime import sleep # Import the sleep method # Green LED led = Pin(15, Pin.OUT) # Button button = Pin(13, Pin.IN, Pin.PULL_DOWN) led.off() # Turn off LED # Setup a callback function to handle button pressed # using an interrupt service routine def flash_led(not_used): for i in range(0, 5): led.on() sleep(0.1) led.off() sleep(0.1) # Register the callback (ISR) button.irq(trigger=Pin.IRQ_FALLING, handler=flash_led) print("Ready for testing!") while True: sleep(0.25) Как только увидите сообщение «Ready for testing!» в консоли REPL, вы можете проверить программу. Если вы пугливы или имеете необычно высокую склонность к статическому электричеству, нажмите кнопку, используя
74  Знакомство с MicroPython непроводящий предмет1, и наблюдайте, как зеленый светодиод мигает, а затем гаснет. Попробуйте несколько раз, пока не убедитесь, что это работает. Итоги MicroPython – очень интересное дополнение к миру микроконтроллеров. Впервые новичкам не нужно изучать новую операционную систему или сложный язык программирования, такой как C или C++, для программирования микроконтроллера. MicroPython позволяет людям с некоторым опытом программирования или даже без него экспериментировать с электроникой и создавать интересные проекты. Таким образом, MicroPython предоставляет возможности большему количеству любителей и энтузиастов, которые прос­ то хотят, чтобы их проекты работали без сложного обучения. В этой главе мы открыли для себя основные возможности MicroPython. Мы также обнаружили, что MicroPython основан на Python, который встречается на наших ПК. Мы даже протестировали Python на ПК, чтобы увидеть сходство. Мы также своими глазами увидели, как работает MicroPython на плате микроконтроллера. Для этого случая мы использовали Raspberry Pi Pico, чтобы продемонстрировать три примера кода Python, написанного для тестирования дискретных компонентов, подключенных к плате. В следующей главе мы познакомимся со множеством аппаратных средств, которые можно использовать для запуска MicroPython и создания наших проектов интернета вещей. Как вы увидите, существует множество вариантов: от плат, на которых уже загружен MicroPython, что позволяет вам создавать свои проекты без дополнительной настройки, до обычных плат с микроконтроллерами, на которые вы можете загрузить MicroPython самостоятельно. 1 Конечно, это шутка со стороны автора: тактовую кнопку вполне можно нажимать пальцем. Даже если вы случайно коснетесь выводов кнопки, платы контроллера или светодиодов, это не будет представлять опасности ни для вас, ни для схемы. Единственный непредсказуемый эффект, возможный с некоторой долей вероятности, – случайное замыкание контактов кнопки влажным пальцем может вызвать срабатывание, как при ее нажатии. – Прим. перев.
Глава 3 Аппаратные средства MicroPython Теперь, когда мы кратко рассмотрели, как можно использовать платы MicroPython, включая несколько разновидностей устройств, пришло время совершить экскурсию по доступным платам и соответствующему оборудованию. Как мы увидим, вы можете выбирать из нескольких вариантов, в том числе плат, на которых уже загружен MicroPython и которые готовы к использованию «из коробки»; плат, позволяющих загружать MicroPython; или некоторых других плат, которые, возможно, стоит научить использовать MicroPython, если немного поработать. В этой главе также описывается, как начать использовать каждую из обсуждаемых плат, в том числе как подключить плату, загрузить встроенное ПО и многое другое. Вам следует прочитать всю главу, потому что хотя многие концепции одинаковы, процедуры могут немного отличаться от одной платы к другой. Кроме того, в этой главе основное внимание уделяется платам, готовым к использованию MicroPython. Хотя я обсуждаю альтернативные платы, которые вы можете использовать, достаточно кратко, информация в этих разделах может быть полезна для использования плат, в этой главе не упомянутых. Прежде чем мы перейдем к рассмотрению доступных плат, давайте обсудим несколько лучших практических рекомендаций по использованию плат MicroPython. Они применимы ко всем платам в этой главе и, вероятно, к любой другой новой плате, поэтому вам следует иметь их в виду при выборе. Начало работы с платами MicroPython Хотя вы можете просто купить одну из этих плат и сразу приступить к своему IoT-проекту, есть некоторые общие вещи, которые вам следует учитывать и иметь в виду при работе. В частности, некоторые платы могут потребовать обновлений, некоторые могут потребовать сборки (пайки), а у других могут
76  Аппаратные средства MicroPython быть ограничения, которые следует учитывать. Об этом и многом другом я расскажу в этом разделе. Я также включил несколько советов по совместному использованию плат с вашим ПК. Обновления прошивки Платам, на которых уже установлен MicroPython, могут потребоваться обновления прошивки (встроенное программное обеспечение, firmware)1. Прошивка – это термин, используемый для описания программного обеспечения, загруженного на чип. Большинство плат имеют специальные чипы, которые позволяют вам самостоятельно обновлять MicroPython (и другие составляющие), что позволяет вам быть в курсе изменений и нововведений. Вам следует рассмотреть возможность обновления встроенного ПО при первом получении платы или в тот момент, когда вы обнаружите, что не можете получить доступ к какой-либо библиотечной функции, не удается выполнить какую-либо операцию или когда выпущено новое оборудование (выносные модули). Тем не менее слишком частого обновления прошивки следует избегать. Моя философия проста: я обновляю прошивку только один раз, когда плата новая, а после этого только тогда, когда что-то больше не работает. Если ваш проект работает и у вас не возникло никаких проблем, обновлять его не нужно. Один из рисков слишком частого обновления прошивки заключается в том, что вы можете сделать свой проект неработоспособным, если что-то изменится в прошивке, например больше не поддерживаются старые биб­ лиотеки или оборудование, произведены изменения в библиотеках, которые нарушат ваш код, и возникнут другие неприятные проблемы. Мое единственное исключение из этого правила – если поставщик исправил ошибку или улучшил взаимодействие с пользователем, и в этом случае я обновлю прошивку. Суть в том, что если что-то работает, не стоит его трогать! СОВЕТ Обновляйте прошивку, когда ваша плата новая, или только тогда, когда это необходимо! Особо следует упомянуть те платы, на которых MicroPython необходимо загружать самостоятельно. Некоторые из этих плат могут потребовать от вас обновления других частей системы, таких как механизм начальной загрузки, способ загрузки MicroPython в энергонезависимую память и т. д. Если вы решите использовать плату, на которую необходимо загружать MicroPython, обязательно проверьте документацию поставщика на наличие рекомендаций по обновлению программного обеспечения на плате (могут не ограничиваться только прошивкой). Наконец, некоторые платы являются новыми, поэтому их прошивка может не находиться в стадии бета-тестирования или 1 https://en.wikipedia.org/wiki/Firmware – Прим. авт. Эквивалентная статья на русском: https://ru.wikipedia.org/wiki/Встроенное_программное_обеспечение. – Прим. перев.
Начало работы с платами MicroPython  77 не быть кандидатом на выпуск, а также не соответствовать производственному качеству. В таких случаях я предпочитаю чаще обновлять прошивку, пока не будет выпущена производственная версия. Проблемы с сетью Если ваша плата имеет сетевые возможности, вам следует проявлять особую осторожность при обучении ее использованию. В частности, не торопитесь, следуя инструкциям поставщика по настройке сети и подключению платы к сети. Например, некоторые платы имеют весьма специфический механизм подключения к сетям Wi-Fi. Неспособность правильно настроить сетевое соединение приведет к большому разочарованию, особенно если ваш проект предназначен для создания данных, доступ к которым осуществляется по сети. Если ваша плата имеет сетевые возможности, я рекомендую прочитать документацию и запустить все примеры, показывающие, как подключить плату к сети. Время, потраченное на изучение того, как это сделать (и повторение примеров), позволит сэкономить много времени, потраченного впустую. Действительно, ошибка номер один, которую допускают новички, – это подключить свою плату и написать код для отправки данных в другую систему в сети, а затем обнаружить, что это не работает. В этом случае им не удалось убедиться, что сетевая часть работает, прежде чем использовать ее в первый раз. Это также относится и к подключению платы к компьютеру. Для тех плат, которые позволяют получить доступ к встроенной памяти как к файловой системе, это может не быть проблемой, но для других плат, требующих подключения через USB с помощью специального программного обеспечения, вы должны убедиться, что можете подключиться к плате, прежде чем пытаться записать свой файл с первой программой на Python. Один шаг за раз! Еще одна очень распространенная ошибка, которую допускают новички, – это сесть и написать весь свой код за один проход, ничего не тестируя заранее. Схема работает с ошибками, и это может происходить по множеству причин в разных местах. Например, если имеется какая-либо логическая ошибка или полученные данные неверны, это может привести к сбою других частей проекта или получению неверных результатов. Ситуация усугубляется, когда проект вообще не работает – в нем слишком много всего, чтобы попытаться диагностировать, что пошло не так. Это часто ставит новичков в отчаянную ситуацию замешательства и разочарования – все равно приходится постепенно разбирать схему до отдельных узлов. Студенты точно знают, о чем я говорю.
78  Аппаратные средства MicroPython Этого можно легко избежать, создавая проект поэтапно. То есть создавайте свой проект по одному шагу за раз. Например, если вы работаете со светодиодами, чтобы о чем-то сигнализировать, сначала заставьте их работать. Точно так же, если вы считываете данные с датчика, убедитесь, что вы можете сделать это правильно в отдельности, прежде чем соединять все вместе и надеяться, что все работает. Даже очень опытные люди могут совершить эту ошибку, но они более подготовлены, чтобы исправить ее, если что-то пойдет не так (и они знают лучше), но нужно делать то, что я говорю, а не то, что я делаю в различных ситуациях. Мы будем строить примеры в этой книге шаг за шагом. Некоторые из них настолько малы, что могут состоять только из одной строки кода, но эту практику следует учитывать в любом проекте, за который вы беретесь. Программные инструменты Некоторые производители микроконтроллеров предлагают свои инструменты разработки программного обеспечения (программирования). Пожалуй, одной из самых успешных является интегрированная среда разработки (IDE) Arduino. Arduino IDE предоставляет все инструменты, необходимые для программирования Arduino, – от написания кода до компиляции и загрузки его на плату. Аналогично лучшая IDE для работы с MicroPython – Thonny. Если вы работали с Raspberry Pi и Python, скорее всего, вы столкнулись с красивой интегрированной средой разработки (IDE) Python под названием Thonny. Thonny доступна для большинства платформ, включая Linux, Windows и macOS (см. https://thonny.org). Просто скачайте установщик для вашей платформы и установите ее. После того как вы установили Thonny и запустили ее в первый раз, на некоторых платформах вам будет предложено выбрать язык и начальные настройки. Варианты настроек включают Standard и Raspberry Pi. Настройки Raspberry Pi упрощены, меню вы не увидите (но его можно включить, переключив режим). Вам следует выбрать опцию Standard, как показано на рис. 3.1. Рис. 3.1  Выбор начальных настроек (Thonny)
Начало работы с платами MicroPython  79 ОСТОРОЖНО! Для выполнения этой процедуры ваш Pico не должен быть подключен к компьютеру. Если это так, отключите его, прежде чем продолжить. Используя Thonny, вы можете разрабатывать код Python и MicroPython и даже запускать его для его тестирования. Редактор предназначен для написания кода Python и содержит множество полезных инструментов, которые помогут вам в написании кода. Более того, все это делается с помощью простого, лаконичного пользовательского интерфейса, элегантного в своей простоте. На рис. 3.2 показан пример использования Thonny для подключения и запуска сценариев MicroPython на плате MicroPython. Обратите внимание, что есть файловый менеджер, редактор и окно консоли REPL. Прекрасно! Рис. 3.2  Thonny IDE Если вы не хотите использовать IDE, большую часть того, что нужно будет сделать для написания скриптов Python, можно выполнить без специального программного обеспечения. Существует несколько программных редакторов, поддерживающих Python, включая Komodo Edit (доступен на сайте www.activestate.com/komodo-ide/downloads/edit), PyCharm (www.jetbrains.com/
80  Аппаратные средства MicroPython pycharm) и плагин PyDev IDE для Eclipse (www.pydev.org). PyCharm IDE предлагается бесплатно, как редактор для членов сообщества, а также как платное приложение. Komodo Edit также является бесплатной версией более мощной платной IDE Komodo Edit. Больше всего мне нравятся редакторы Komodo Edit и PyCharm. Оба имеют подсветку синтаксиса Python – цвет текста меняется для выделения разных элементов языка: служебных слов, строк и т. д., а в некоторых случаях они могут дополнять ваши операторы при вводе и автоматически делать отступы в зависимости от того, какую конструкцию вы используете. Оба являются отличным выбором для написания Python. Komodo Edit – простой редактор, но он отлично справляется со своей задачей. PyCharm – это среда разработки Python, которая делает гораздо больше, в том числе позволяет интерактивную отладку. Однако вы не сможете использовать некоторые функции плат MicroPython, потому, если вы хотите работать с Python на своем ПК, вам следует рассмотреть возможность использования Thonny IDE (или аналогичной ей). Хотя это и не обязательно, настоятельно рекомендуется использовать редактор, включающий подсветку синтаксиса Python. Это не только поможет написать код, но также может оказаться полезным для написания более правильного кода. Функция завершения кода экономит минуты и часы в реальном времени. Помните, что написание Python для платы MicroPython аналогично написанию сценария Python для вашего ПК – синтаксис тот же. Требуется доработка Некоторые поставщики предлагают платы с припаянными разъемами или без них. Отсутствие пайки разъемов позволяет сэкономить на производстве, а в некоторых случаях и на доставке, и делает платы немного дешевле1. Если вы умеете паять (или знаете кого-то, кто это умеет), возможно, вам удастся немного сэкономить, выбрав платы без разъемов. Еще одна причина, по которой вам может понадобиться плата без разъемов, – установка платы в корпус готового устройства или использование в какой-либо другой форме встроенной установки (например, с применением главной кросс-платы)2. В этом случае пайка разъемов может занять больше места, чем у вас есть, или сделать завершенный проект более громоздким. 1 2 Что может быть еще важнее – при самостоятельной пайке можно использовать разъемы различной конфигурации (гнезда вместо штырей, угловые разъемы, разъемы с удлиненными контактами и т. д.) – это может быть удобным при монтаже платы в готовое устройство. – Прим. перев. Для случая установки без разъемов на платах по краям у каждого контакта оставляют металлизированные выемки, с помощью которых контакты припаивают к площадкам на главной плате устройства (см. также далее рис. 3.10 и его описание в тексте). – Прим. перев.
Начало работы с платами MicroPython  81 Вы также можете столкнуться с некоторыми дополнительными модулями, коммутационными платами или другими отдельными компонентами, к которым не припаяны разъемы. Если вы хотите их использовать, возможно, вам также придется припаять разъем самостоятельно. Например, большинство коммутационных плат от Adafruit и SparkFun с припаянными разъемами не поставляются. Контакты GPIO В предыдущей главе мы немного узнали о контактах (выводах) ввода/вывода общего назначения (GPIO). Одна из вещей, которая отличает различные платы, – это то, как расположены выводы GPIO и их количество. Большинство плат поддерживают однотипный перечень выводов по назначению, включая питание, аналоговые и цифровые выводы. Некоторые платы предоставляют меньше или больше выводов, чем другие. Если для вашего проекта требуется несколько выводов – аналоговых или цифровых, – вам следует запланировать выбор платы, которая имеет необходимое количество контактов. К счастью, для большинства применений обычных плат достаточно; однако некоторые из новых плат, на которые можно загрузить MicroPython, могут этому требованию не соответствовать. Например, некоторые версии вездесущих ESP8266 или ESP321 (недорогих чипов с поддержкой Wi-Fi) могут поддерживать только несколько контактов, что делает их далеко не идеальным выбором для проектов, в которых требуется много выводов. На рис. 3.3 приведена иллюстрация, иллюстрирующая выводы GPIO, доступные на Raspberry Pi Pico. Вы можете найти аналогичные схемы (также называемые схемами разводки выводов или просто распиновками) у производителя или поставщика вашей платы. Другие советы В этом разделе содержится несколько советов о том, с чем вы можете столк­ нуться при работе с платой MicroPython. Эти советы следует принимать во внимание до начала или во время экспериментов. Начнем с основ. Посетите форумы сообщества Самое первое, что вам следует сделать (даже перед покупкой), – это посетить форумы сообщества, посвященные выбранной вами плате. Многие поставщики размещают онлайн-форум, на котором люди могут задавать вопросы, 1 См. на русском языке статьи https://ru.wikipedia.org/wiki/ESP8266 и https://ru.wikipedia. org/wiki/ESP32. – Прим. перев.
82  Аппаратные средства MicroPython а члены сообщества могут делиться своими идеями, ответами, предложения­ ми и даже помогать с затруднениями. Рис. 3.3  Разводка выводов GPIO Raspberry Pi Pico (с разрешения raspberrypi.com) Помимо чтения этой книги, посещение форума сообщества является абсолютной необходимостью. Фактически вам следует посещать форум каждый раз, когда у вас возникают вопросы или проблемы с платой. Скорее всего, вы встретите кого-то, кто уже сталкивался с такой же (или похожей) проблемой, и получите от него и других членов сообщества предложения о том, как ее решить. Ниже перечислены форумы для наиболее популярных плат MicroPython. Если вы не видите здесь свою плату, найдите ее в Google, когда в следующий раз будете онлайн; затем, если вам нравится форум, добавьте его в закладки для более быстрого доступа:   Raspberry Pi Pico: www.raspberrypi.com/products/raspberry-pi-pico;   Arduino Nano RP2040: https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-01-technical-reference;   форум CircuitPython: https://forum.micropython.org/viewtopic.php?f= 16&t=2894;   общий форум Python (не только для MicroPython): www.python.org/community. ПРИМЕЧАНИЕ Дополнительные советы по взаимодействию с форумами сообществ я включил в главу 16.
Начало работы с платами MicroPython  83 Обращаться осторожно! Вам следует рассматривать вашу плату MicroPython как чувствительное устройство, подверженное электростатическому разряду. Если вы еще не установили плату в корпус, с ней следует обращаться осторожно, всегда помещая ее на непроводящую поверхность перед включением питания. Статический разряд в этих условиях может быть вызван многими причинами (синтетическая одежда и обувь, хождение по синтетическому ковру, ламинату или линолеуму и т. п.). Этот разряд может повредить плату. Всегда следите за тем, чтобы вы обращались с платой так, чтобы электростатический разряд контролировался и его вероятность был сведена к минимуму. Вы также никогда не должны перемещать плату, когда она включена. Почему? Любые платы имеют припаянные компоненты со множеством контактов, открытых с обеих сторон. Если любые два или более из этих контактов соприкоснутся с чем-то, проводящим электричество, вы рискуете повредить плату1. Кроме того, всегда храните плату в контейнере, защищенном от электростатического разряда, специально предназначенном для хранения электроники. Следует избегать обычных бытовых пластиковых коробок. Однако если у вас нет такого контейнера, вы можете использовать пакеты, защищенные от статического электричества, чтобы поместить плату на время ее хранения. Многие платы и компоненты, которые вы покупаете, поставляются в такой упаковке2. Так что не выбрасывайте ее! Вам следует позаботиться о том, чтобы ваше тело, ваше рабочее место и ваш проект были заземлены, дабы избежать электростатического разряда (ESD). Лучший способ избежать этого – использовать заземляющий браслет, который обхватывает запястье и крепится к антистатическому коврику3. Наконец, будьте особенно осторожны при подключении USB-кабеля к плате. Большинство плат оснащены разъемом micro-USB, который подвержен поломке в большей степени, чем другие разъемы. В большинстве случаев 1 2 3 Также представляют опасность проводные перемычки, неплотно держащиеся в гнездах макетной платы: при перемещении они могут выскочить и замкнуться на открытые площадки платы контроллера. Особенно неприятные последствия могут наступить, если выскакивают и начинают свободно болтаться выводы питания или общего провода. – Прим. перев. Такую упаковку легко отличить от обычной по наличию темного блестящего металлизированного покрытия. Эффективный метод безопасного хранения компонента с выводами (в том числе платы с установленными разъемами) – воткнуть его в специальный вспененный пластик (пенопласт или пенополистирол) с содержанием графита, легко отличимый от обычного по интенсивному черному цвету. – Прим. перев. Такой комплект несложно приобрести в отечественных интернет-магазинах (в т. ч. даже на «Озоне»), но следует учесть, что фирменные подобные изделия довольно дороги, а дешевый «нонейм» вызывает обоснованные сомнения в его эффективности. Опыт показывает, что для любительских работ подобное решение избыточно – обычно достаточно заземленного паяльника и одежды из натуральных материалов (хлопка или шерсти). – Прим. перев.
84  Аппаратные средства MicroPython ломается не шлейф на кабеле, а разъем на самой плате. В этом случае ремонт может быть очень сложным (или плата вообще не подлежит ремонту). Также возможно, что сам кабель перестанет работать или будет работать только тогда, когда вы удерживаете его на месте. Если это произойдет, попробуйте новый кабель и, если это решит проблему, выбросьте старый. Если это не решит проблему, возможно, дело в разъеме на плате. К счастью, если проявить осторожность при подключении и отключении кабеля, этих проблем можно избежать. Например, всегда сначала подключайте сторону micro-USB, а затем используйте полноразмерный разъем USB-A для подключения и отключения от компьютера. Чем меньше раз вы используете разъем micro-USB, тем меньше у вас шансов его повредить. Неправильные настройки переключателей Если вы работаете с платами расширения, то можете столкнуться с платами с переключателями, позволяющими менять настройки электропитания. Например, Grove Shield для Pico от Seeed Studio (www.seeedstudio.com/ Grove-Shield-for-Pi-Pico-v1-0-p-4846.html) позволяет вам выбирать между 3,3 В и 5 В для питания модулей, подключенных к плате (как показано на рис. 3.4). Обязательно проверьте требования к питанию для всех используемых модулей и проверьте переключатель, чтобы убедиться, что он находится в правильном положении, прежде чем подавать питание на плату. Неправильные настройки могут повредить некоторые модули. Рис. 3.4  Переключатели на плате Grove Shield for Pico (с разрешения seeedstudio.com)
Начало работы с платами MicroPython  85 Ослабленные или отсутствующие джамперы Еще одна вещь, которая может случиться при использовании платы с перемычками-джамперами (небольшими прямоугольниками, предназначенными для замыкания цепи между контактами игольчатых разъемов), – это потеря одной или нескольких перемычек. Такие перемычки используются для включения или отключения определенных функций. На рис. 3.5 показан пример того, как выглядят джамперы и их контакты. Рис. 3.5  Джамперы (с разрешения сайта sparkfun.com) Джемперы настолько мелкие, что их легко потерять или положить не на место. Если вы потеряете перемычки и вам потребуется замена, их обычно можно приобрести в ближайшей к вам мастерской по ремонту компьютеров. Если в вашем районе нет мастерских по ремонту компьютеров, если вы найдете энтузиаста ПК, который собирает свои собственные ПК, то сможете получить у него несколько перемычек. Почему? Потому что у этих парней их обычно полно. Суть в том, что вам не придется их покупать – я уверен, что у кого-то из вашего окружения есть несколько запасных. Если одна или несколько перемычек отвалились, вы можете затянуть их, чтобы их было труднее снять. Чтобы затянуть перемычки, используйте острогубцы и аккуратно (я уже говорил аккуратно?) сожмите гнезда. Для этого есть небольшая хитрость. Вы также можете слегка отогнуть перемычки в верхней части штыря, чтобы они прилегали более плотно. Конечно, вы можете найти этого компьютерного энтузиаста и купить новую, если она все еще не закреплена или вы ее повредили1. 1 Все описанные автором сложности с добыванием или ремонтом перемычек совершенно лишние, если учесть, что контактами для джамперов служат обычные игольчатые разъемы типа PLS с шагом 2,54 мм (см. рис. 3.5). Поэтому для изготовления надежной перемычки достаточно взять гнездовую PBS-часть с парой контактов (можно отпилить или аккуратно откусить нужный фрагмент от длинного разъема) и замкнуть их между собой с помощью пайки любым проводником (удобно применять обрезки выводов от резисторов). – Прим. перев.
86  Аппаратные средства MicroPython Перемычки также могут выглядеть как небольшие паяные соединения для поверхностного монтажа, где «перемычка» соединяет две или более площадки для включения функции или изменения настройки. Незапаянная перемычка считается «разомкнутой», или «выключенной», тогда как две или более площадки, перемкнутые с помощью капли припоя, считаются «замкнутыми», или «включенными». На рис. 3.6 показан пример двух паяных перемычек. Та, что слева, включена (запаяна), а та, что справа, не запаяна (выключена). Рис. 3.6  Паяные перемычки (с разрешения сайта sparkfun.com) Теперь, когда мы рассмотрели некоторые соображения по началу работы с платами MicroPython, давайте начнем обзор доступных плат, начиная с тех, на которых MicroPython загружен и готов к использованию. Каждый раздел включает краткий обзор платы, а также примечания о том, где ее можно приобрести и как начать использовать. Платы MicroPython RP2040 Первая категория плат, которую мы рассмотрим, – это платы с установленным MicroPython, использующие чип Raspberry Pi RP2040. Для использования этих плат вам не нужно устанавливать какое-либо программное обес­ печение (за исключением того, что вам может потребоваться периодически обновлять прошивку или установить драйвер на свой компьютер). Таким образом, эти платы – лучший выбор для новичков в MicroPython и электронике интернета вещей в целом. Фактически проекты в этой книге демонстрируются в основном с использованием этих плат, и вы обнаружите, что их более чем достаточно для большинства малых и средних проектов интернета вещей. На момент написания этой книги плат, готовых к использованию с MicroPython, было множество, но мы будем использовать две самые популярные платы: Raspberry Pi Pico и Arduino Nano RP2040 Connect. Оба варианта являются хорошим выбором, и оба используют революционный чип Raspberry Pi RP2040. Как мы увидим, один из этих вариантов немного проще использовать для IoT-проектов. Давайте сначала выясним, почему этот чип (и платформа Raspberry Pi в целом) важен для проектов MicroPython.
Платы MicroPython RP2040  87 Происхождение RP2040 Фонд Raspberry Pi (raspberrypi.org) изменил мир, предоставив мощные и недорогие компьютерные платы. Raspberry Pi на сегодняшний день является самой продаваемой и самой популярной из многих доступных одноплатных компьютерных платформ. Возможно, еще важнее то, что Raspberry Pi ориентирован на образование. Преподаватели могут использовать Raspberry Pi для обучения информатике, электронике, автоматизации оборудования и создания проектов интернета вещей с использованием языков программирования Python, Java или C++. Более того, возможность запуска мощной операционной системы для настольного компьютера означает, что вы можете использовать Raspberry Pi так же, как свой ноутбук или настольный компьютер, для создания своего проекта и подключения его к другому оборудованию через выводы общего назначения (GPIO). Благодаря этим преимуществам глобальное доминирование платформы Raspberry Pi было лишь вопросом времени. Семейство микроконтроллеров Raspberry Pi Pico (www.raspberrypi.com/ products/raspberry-pi-pico) представляет собой отход от доминирования небольших компьютерных плат Raspberry Pi, поскольку не является еще одним одноплатным компьютером. У этой платы нет возможности запускать операционную систему; здесь нет ни видеопортов, ни хост-портов USB, ни даже специального разъема питания. Raspberry Pi Pico – это первый микроконтроллер, в котором используется чип RP2040 на базе Raspberry Pi. СОВЕТ Да, вы можете запрограммировать Raspberry Pi Pico со своего компьютера Raspberry Pi! Почему это важно? Такая ситуация означает, что Raspberry Pi – один из новейших конкурентов в области микроконтроллеров, и, как мы увидим, фонд Raspberry Pi принял вызов, выпустив мощный и доступный микроконтроллер, работающий под управлением MicroPython (Python для микроконтроллеров), что упростило программирование и использование Raspberry Pi Pico. Представляем RP2040 Начнем с названия. На первый взгляд название может показаться странным. Обычно мы думаем, что это какая-то редакция или версия1, но это не относится к RP2040. На рис. 3.7 показана расшифровка названия. Как видите, в нем закодированы четыре характеристики микроконтроллера. Возможно, в будущем мы увидим варианты этого чипа, и следует ожидать, что название будет соответственно меняться. 1 Нет, это не открытие Эдисона, где до получения результата испробовано 2039 неудачных версий. – Прим. перев.
88  Аппаратные средства MicroPython Энергонезависимая память (0 – отсутствует) Условный объем RAM (log2(RAM) / 16 Кбайт) Условный тип ядра (0 = Cortex M0+) Число ядер Raspberry Pi Рис. 3.7  Расшифровка названия RP2040 (с разрешения raspberrypi.org) RP2040 – это один чип, объединяющий память, двухъядерный процессор, интерфейсы и вспомогательную электронику. Чип создан для обеспечения высокой производительности при низком энергопотреблении. Фактически он также может поддерживать расширенное выполнение с использованием энергии аккумулятора. И что самое приятное, он может похвастаться возможностью запускать MicroPython, что делает программирование очень простым, устраняя необходимость обучения программированию, характерную для микроконтроллеров. Другими словами, вам не обязательно иметь степень в области программирования или электроники, чтобы иметь возможность его использовать. Основные функции RP2040 перечислены ниже:   два ядра ARM Cortex-M0+ @ 133 МГц;   264 Кбайт встроенной SRAM в шести независимых банках;   поддержка до 16 Мбайт внешней флеш-памяти через выделенную шину QSPI;   контроллер прямого доступа к памяти;   полностью подключенная шина AHB1;   периферийные устройства интерполятора и целочисленного делителя;   встроенный программируемый LDO-стабилизатор2 для генерации напряжения ядра;   две встроенные схемы ФАПЧ3 для генерации тактовой частоты USB и ядра;   30 выводов GPIO, четыре из которых можно использовать как аналоговые входы;   периферийные устройства включают в себя: – 2 порта UART; 1 2 3 Advanced High-performance Bus, усовершенствованная высокопроизводительная шина. – Прим. перев. Low Drop Out – стабилизатор с низким падением напряжения. – Прим. перев. ФАПЧ (PLL) – фазовая автоподстройка частоты. – Прим. перев.
Платы MicroPython RP2040  89 – – – – – 2 SPI-контроллера; 2 контроллера I2C; 16 каналов ШИМ; контроллер USB 1.1 и PHY1 с поддержкой хоста и устройства; 8 конечных автоматов PIO2. Итак, что же это такое? Для большинства эти функции могут не иметь большого значения, но, по сути, мы говорим о серьезно продвинутом чипе. К функциям, которые могут вас больше всего заинтересовать, относятся контроллеры SPI и I2C (по 2 штуки каждого), 16 каналов импульсной модуляции (ШИМ) и 30 контактов GPIO. Достаточно сказать, что этот контроллер может справиться практически со всем, что вам может понадобиться для вашего проекта в области электроники. Прекрасно! Микропроцессор RP2040 можно приобрести отдельно, и число поставщиков, создающих платы на базе RP2040, растет. Мы увидим некоторые из них в следующем разделе. Но сначала давайте рассмотрим аппаратную часть Pico более подробно. Raspberry Pi Pico / Pico W Raspberry Pi Pico W (https://thepihut.com/products/raspberry-pi-pico-w), вариант Pico, представляет собой небольшую зеленую печатную плату размером с пачку жевательной резинки. Вдоль каждой длинной стороны расположены контакты выводов GPIO и разъем micro-USB на одной из коротких сторон. На другой короткой стороне (или посередине платы) находится набор отладочных контактов (SWD), которые можно использовать для расширенной диагностики. На рис. 3.8 показан вариант Pico W с портом USB справа, с чипом Wi-Fi (под большой блестящей крышкой), отладочными контактами посередине и антенной, расположенной слева. Обратите внимание на разъемы GPIO сверху и снизу. Три контакта под надписью «DEBUG» – это выводы отладки. Единственный другой компонент на плате, о котором нам нужно знать, – кнопка BOOTSEL (выбор загрузки), расположенная в правом верхнем углу рисунка. Эта кнопка используется для перевода Pico в режим загрузки, когда или запускается платформа MicroPython, или, если кнопку удерживать нажатой, USB-кабель подключен к компьютеру, плата подключается как съемный диск, что позволяет загружать новые файлы или изменять файлы базовой платформы. Позже в этом разделе мы увидим, как это сделать. 1 2 PHY (от англ. Physical layer, физический уровень) – интегральная схема, предназначенная для выполнения функций физического уровня сетевой модели OSI. – Прим. перев. Programmed input–output, программируемый ввод/вывод. Выводы конечных автоматов PIO позволяют программным путем конструировать произвольные интерфейсы (добавлять как дополнительные UART/SPI/I2C, так и отсутствующие в Pico DVI, 1-Wire и пр.). – Прим. перев.
90  Аппаратные средства MicroPython Рис. 3.8  Raspberry Pi Pico W – вид сверху (с разрешения raspberrypi.org) На рис. 3.9 показана нижняя часть обычного Pico (без Wi-Fi). Обратите внимание, что с этой стороны контакты GPIO помечены, что позволяет легко найти конкретный контакт. Площадки, помеченные буквой «TP», представляют собой контрольные точки, которые можно использовать для проверки напряжений и сигналов, если вам потребуется выполнить расширенную диагностику платы. Опять же, контакты слева предназначены для интерфейса последовательной проводной отладки (SWD). Мы не будем использовать интерфейс SWD в этой книге, но вы можете прочитать больше о нем в главе 6 описания работы с Pico: https://datasheets.raspberrypi.org/pico/getting-startedwith-pico.pdf. Рис. 3.9  Raspberry Pi Pico – вид снизу (с разрешения raspberrypi.org) Как вы поняли, буква «W» в названии означает, что эта версия Pico оснащена Wi-Fi и, таким образом, Pico можно подключить к своей локальной сети и интернету. Отлично!
Платы MicroPython RP2040  91 СОВЕТ Первая версия Raspberry Pi Pico не включает Wi-Fi, но использует тот же процессор и такие же выводы GPIO (https://thepihut.com/products/raspberry-pi-pico). Таким образом, вы можете использовать более старую версию Pico (без Wi-Fi) для многих проектов в этой книге, которые не требуют подключения к интернету. Сердцем Pico является большая черная микросхема, расположенная в цент­ ре платы на верхней стороне. Это микропроцессор RP2040, обеспечивающий все функции Pico. Обзор аппаратной части Итак, что такое Pico? Это печатная плата, построенная на основе RP2040 вместе со вспомогательными схемами в виде небольшой платы размером с пачку жевательной резинки. Она имеет выводы всех интерфейсов и GPIO, поддерживаемых RP2040, а также контакты питания и общего провода. Pico – это недорогая плата, предлагающая больше функций, чем любая другая плата в этом ценовом диапазоне. Фактически вы можете найти Pico всего за 4 доллара! Это потрясающе, учитывая то, что вы получаете. За эту цену вы получите Pico без припаянных разъемов и можете купить разъемы (штыревые или гнездовые), отдельно обрезанные по длине. Если вы не умее­те паять, вы можете приобрести Pico с припаянными разъемами на пару долларов дороже. Несмотря на это, стоимость все равно намного ниже той суммы, которую вы ожидаете заплатить за полнофункциональную плату микроконтроллера1. Давайте обратим внимание на выводы разъема. Если вы присмотритесь, то увидите, что площадки состоят из двух рядов отверстий: одно отверстие ближе к центру платы, а другое половинное отверстие на краю, что придает длинным краям платы зазубренный вид. Эта конструкция позволяет на выбор распаять плату способом поверхностного монтажа или установить штыревые разъемы для использования с макетной платой, или гнездовые разъемы, чтобы можно было использовать перемычки для подключения компонентов к Pico. На рис. 3.10 отверстия под разъем показаны более подробно. СОВЕТ Полное руководство о том, как припаять разъемы к Raspberry Pi Pico, можно найти на странице https://magpi.raspberrypi.org/articles/how-to-solder-gpio-pin-headers-to-raspberry-pi-pico. Помимо встроенных функций RP2040, Pico имеет следующие особенности:   микроконтроллер RP2040, разработанный Raspberry Pi в Великобритании на основе двухъядерного процессора ARM Cortex M0+ с изменяемой тактовой частотой до 133 МГц;   264 Кбайта SRAM и 2 Мбайта встроенной флеш-памяти; 1 В отечественных интернет-магазинах стоимость Pico существенно выше эквивалента 4 долларов, но все равно она примерно равна или даже ниже стоимости базовых Arduino, имеющих куда меньшие возможности. – Прим. перев.
92  Аппаратные средства MicroPython Рис. 3.10  Крупный план отверстий под разъем на Pico (с разрешения raspberrypi.org)   беспроводной чип Infineon CYW43439;   беспроводная локальная сеть Wi-Fi с поддержкой стандарта IEEE 802.11n;   поддержка WPA31;   режимы сна и сна с низким энергопотреблением;   перезапись программ с использованием запоминающего устройства через USB;   печатная плата 21×51 мм толщиной 1 мм со сквозными выводами диаметром 0,1 дюйма, а также с зубцами по краям;   40 выводов на разъемы;   26 многофункциональных входов/выходов общего назначения (GPIO) с напряжением 3,3 В;   23 вывода GPIO предназначены только для цифровых сигналов, остальные три имеют поддержку АЦП;   возможен поверхностный монтаж в виде модуля;   3-контактный последовательный порт отладки ARM (SWD);   порт micro-USB для питания и передачи данных (а также для перепрограммирования флеш-памяти);   два варианта питания: через micro-USB или от внешнего блока питания/батареи;   поддержка хоста и устройств USB 1.1;   часы реального времени (RTC);   датчик температуры;   комплексный SDK, примеры программного обеспечения и документация. 1 WPA3 – 3-я версия стандарта безопасности WPA для вычислительных устройств с беспроводным подключением к интернету. – Прим. перев.
Платы MicroPython RP2040  93 СОВЕТ Полное описание функций Pico см. в документации по адресу https://datasheets.raspberrypi.org/pico/pico-datasheet.pdf1. Начало работы Для подключения к плате используйте кабель USB type A – micro USB, подключающийся одним концом к Pico, другим к компьютеру. Если это новый Pico, то при первом подключении Pico к компьютеру через несколько секунд вы должны увидеть смонтированный диск с именем RPI-RP2 (в Проводнике Windows появится новая буква диска). Когда вы видите свою плату как USBнакопитель, это означает, что плата загрузилась в режиме USB-накопителя, который позволяет копировать файлы и загружать прошивку. Загрузка прошивки Есть две основные причины для загрузки новой прошивки. Во-первых, вам может потребоваться перезагрузить прошивку, если вы использовали плату в нескольких проектах и хотите сбросить ее настройки. Во-вторых, вы можете периодически загружать новую прошивку. Это относится ко всем платам, независимо от того, поддерживают ли они MicroPython. Это связано с тем, что версия MicroPython продолжает дорабатываться и дефекты исправляются. Таким образом, чтобы обеспечить наличие самых последних обновлений, вам следует обновить прошивку хотя бы один раз при получении платы и при необходимости позже, например когда в библиотеку добавляется новый аппаратный компонент. Загрузить прошивку с помощью Thonny IDE легко, но некоторые платы, поддерживающие MicroPython, могут потребовать загрузки вручную. Если вы не можете использовать Thonny, посетите веб-сайт поставщика платы, чтобы найти инструкции по установке прошивки. Загрузка прошивки MicroPython с помощью Thonny состоит из нескольких шагов. Сначала вы должны перевести плату в режим USB-носителя, затем использовать Thonny для загрузки и установки прошивки и, наконец, дождаться перезагрузки платы. Переведите плату в режим USB-носителя Чтобы перевести плату в режим USB-носителя, необходимо разорвать соединение с ПК, затем нажать и удерживать кнопку BOOTSEL (выбор загрузки), расположенную рядом с разъемом USB на Pico, как показано на рис. 3.11. Удерживая кнопку BOOTSEL, подключите плату к компьютеру с запущенным Thonny. При обнаружении USB-накопителя вы должны услышать звуковой сигнал вашего компьютера (если он у вас включен). Когда вы услышите звуковой сигнал, вы можете отпустить кнопку BOOTSEL. 1 Соответственно, для Wi-Fi-версии Raspberry Pi Pico W описание доступно по адресу: https://datasheets.raspberrypi.com/picow/pico-w-datasheet.pdf. – Прим. перев.
94  Аппаратные средства MicroPython Рис. 3.11  Расположение кнопки BOOTSEL (Pico W) Установка прошивки через Thonny В Thonny щелкните в правом нижнем углу и выберите Install MicroPython… (Установить MicroPython…), как показано на рис. 3.12. Рис. 3.12  Установка MicroPython (Thonny) Щелкнув этот пункт меню, вы увидите диалоговое окно Install or update MicroPython (Установить или обновить MicroPython), показанное на рис. 3.13. В этом диалоговом окне вы должны увидеть название флеш-диска, установленное для вашей платы (RPI-RP2). Затем обязательно выберите RP2 для семейства MicroPython, а потом выберите правильный вариант (Pico или Pico W). Если все выбрано верно, нажмите Install (Установить). Диалоговое окно изменится, показывая ход установки, как видно на рис. 3.14. После завершения установки нажмите кнопку Close (Закрыть), как показано на рис. 3.15.
Платы MicroPython RP2040  95 Рис. 3.13  Диалоговое окно установки или обновления MicroPython (UF2) (Thonny) Рис. 3.14  Процесс установки прошивки (Thonny)
96  Аппаратные средства MicroPython Рис. 3.15  Установка прошивки завершена (Thonny) Переподключение/перезагрузка После завершения установки ваша плата перезагрузится, и Thonny подключится снова. Если плата не подключается, щелкните в правом нижнем углу Thonny и выберите свою плату из списка. Если Thonny все равно не распо­ знает плату, вы можете отключить и снова подключить плату к компьютеру. В Thonny вы должны увидеть появление платы и подключение консоли REPL. Вы можете использовать следующий код, чтобы проверить загруженную версию прошивки: >>> import os >>> os.uname() (sysname='rp2', nodename='rp2', release='1.20.0', version='v1.20.0 on 2023-04-26 (GNU 12.1.0 MinSizeRel)', machine='Raspberry Pi Pico W with RP2040') >>> Здесь мы видим версию прошивки 1.20.0, которая на момент написания статьи была последней доступной. Здесь также отображаются дата загрузки прошивки и имя файла прошивки. Наконец, мы видим еще название доски. Ваш первый скрипт MicroPython Теперь, когда на плату загружена последняя версия прошивки, мы можем попробовать написать наш первый скрипт. Если вы следовали инструкциям предыдущих глав и ознакомились с примерами, то, вероятно, уже запускали некоторые фрагменты кода. Ниже – пример полного простейшего сценария MicroPython для вывода в консоли REPL. Да, это необходимый и достаточный
Платы MicroPython RP2040  97 код «Hello, World!», который каждый программист изучает в качестве первого шага1. Код представляет собой просто одну строку следующего вида: print("Hello, World!") Вместо того чтобы запускать этот код в консоли REPL, мы сохраним его в файл. В Thonny с подключенной платой выберите меню File (Файл) ⇒ New (Новый). Вы увидите новый файл, как показано на рис. 3.16. Введите строку кода, показанную ранее. Рис. 3.16  Новый файл (Thonny) Затем щелкните меню File (Файл) ⇒ Save (Сохранить) и выберите место для хранения вашей программы. В данном случае выберите Pico, как показано на рис. 3.17. Затем нажмите кнопку Run (Выполнить), как представлено на рис. 3.18, и наблюдайте за результатом в консоли REPL. Если вы видите результат, как показано на изображении, поздравляем! Вы написали свой первый скрипт (программу) MicroPython, сохранили его на своей плате и выполнили с нее. 1 Рис. 3.17  Выбор места сохранения (Thonny) Некоторые возненавидели этот пример, но он служит совершенно необходимой цели, поскольку помогает убедиться в том, что среда разработки (в данном случае Thonny) настроена правильно. – Прим. авт.
98  Аппаратные средства MicroPython Рис. 3.18  Запуск кода (Thonny) Arduino Nano RP2040 Connect Плата Arduino Nano RP2040 Connect – одна из самых ожидаемых новых плат RP2040. Почему? Потому что Arduino был королем среди микроконтроллеров на любительском рынке. Если вы использовали какой-либо микроконтроллер, скорее всего, это был Arduino или вариант Arduino. Неудивительно, что компания Arduino.cc захотела использовать RP2040 в своем собственном формате микроконтроллеров. Arduino разместила RP2040 на плате формата Nano со всеми функциями Arduino Nano, а также модулем NINA WiFi и Bluetooth. Здесь так много функций, что неудивительно, что на эту плату возлагаются большие надежды. Она немного меньше, чем Pico, с меньшим количеством выводов GPIO, но, подобно Pico, также имеет зубчатый разъем и такой же разъем micro-USB. Хотя Arduino Nano RP2040 Connect можно запрограммировать с помощью Arduino IDE на C-подобном языке Arduino, включая даже функции, позволяющие использовать плату для машинного обучения, в этой книге мы будем использовать плату с Mic­roPython. На рис. 3.19 показана плата Arduino Nano RP2040 Connect. Полное описание этой платы можно найти по адресу https://store.arduino. cc/nano-rp2040-connect-with-headers. Обзор оборудования Arduino Nano RP2040 Connect похожа на Raspberry Pi Pico, поскольку использует тот же чип RP2040. Однако есть компоненты и функции, которые
Платы MicroPython RP2040  99 Рис. 3.19  Arduino Nano RP2040 Connect (с разрешения arduino.cc) отличаются от Raspberry Pi Pico. Вместо повторения одних и тех же функций ниже перечислены различия между платами:   модуль Wi-Fi/Bluetooth U-blox Nina W102: – IEEE 802.11b/g/n однодиапазонный Wi-Fi 2,4 ГГц; – Bluetooth 4.2;   4 АЦП 12-бит;   3 порта I2C, порты SDIO, CAN, QSPI;   память: AT25SF128A 16 Мбайт NOR Flash;   6-осевой МЭМС-модуль 3D-акселерометра / 3D-гироскопа (LSM6DSOXTR): – 3D-гироскоп ±2/±4/±8/±16 г по полной шкале; – 3D-акселерометр ±125/±250/±500/±1000/±2000 точек в секунду в полной шкале; – усовершенствованный шагомер, детектор шагов и счетчик шагов; – обнаружение значительного движения, обнаружение наклона; – программируемый конечный автомат: акселерометр, гироскоп и внешние датчики; – встроенный датчик температуры;   микрофон (MP34DT06JTR): – отношение сигнал/шум 64 дБ; – всенаправленная чувствительность; – чувствительность –26 дБFS ± 1 дБ;   RGB-светодиод: – подключен к U-blox Nina W102 GPIO;   криптографический сопроцессор с безопасным аппаратным хранилищем ключей: – микрочип ATECC608A Crypto; – I2C, SWI; – аппаратная поддержка симметричных алгоритмов: • SHA-256 и HMAC Hash, включая сохранение/восстановление внешнего контекста;
100  Аппаратные средства MicroPython • AES-128: шифрование/дешифрование, умножение поля Галуа для GCM;   встроенный высококачественный генератор случайных чисел (RNG) NIST SP 800-90A/B/C;   выводы GPIO: – 14 цифровых контактов; – 8 аналоговых контактов. Пожалуй, наиболее существенным отличием для тех, кто выбирает эту плату вместо Pico, является разводка выводов GPIO. На рис. 3.20 показана разводка выводов платы Arduino Nano RP2040 Connect. Рис. 3.20  Разводка выводов Arduino Nano RP2040 (с разрешения arduino.cc) Начало работы Вы можете запрограммировать Arduino Nano RP2040 Connect с помощью Thonny после установки прошивки MicroPython. На новые платы не загружена прошивка, поэтому перед использованием вам необходимо будет установить прошивку MicroPython. Как и Pico, вы можете перевести плату в режим USB-накопителя, который позволяет копировать файлы и загружать прошивку. Однако процесс отличается от Pico.
Платы MicroPython RP2040  101 Загрузка прошивки Чтобы установить прошивку на Arduino Nano RP2040 Connect, необходимо сначала загрузить себе последнюю версию файла прошивки, перевести плату в режим USB-носителя и вручную скопировать файл на плату, а затем перезагрузить плату. Загрузка последней прошивки Чтобы загрузить файл прошивки для Arduino Nano RP2040 Connect (файл с расширением .uf2), перейдите по адресу https://docs.arduino.cc/micropython/, прокрутите вниз и разверните раздел выбранной платы, как показано на рис. 3.21. Выберите последний стабильный файл и скачайте его. Например, мы выберем файл с именем 20230426-v1.20.0.uf2, как показано на рисунке. Рис. 3.21  Загрузка прошивки (Arduino Nano RP2040 Connect) Перевод платы в режим USB-носителя Чтобы перевести плату в режим USB-носителя, необходимо сначала подключить плату к компьютеру, а затем быстро дважды нажать кнопку BOOTSEL («Выбор загрузки»), расположенную рядом с разъемом USB на плате, как показано на рис. 3.22. Вы должны услышать звуковой сигнал на своем компьютере и увидеть, как диск RPI-RP2 появится в Проводнике. Копирование файла на диск RPI-RP2 Теперь вы можете скопировать файл на диск с помощью Проводника. По завершении на вашей плате (диске) должен быть один файл, указанный на рис. 3.23.
102  Аппаратные средства MicroPython Рис. 3.22  Расположение кнопки BOOTSEL (Arduino Nano RP2040 Connect) Рис. 3.23  Файл прошивки скопирован в Arduino Nano RP2040 Connect Переподключение/перезагрузка После завершения установки перезагрузить плату можно, если один раз нажать кнопку BOOTSEL, после чего подключится Thonny. Если плата не подключается, щелкните ее в правом нижнем углу Thonny и выберите свою плату из списка. Если Thonny не обнаруживает плату, вы можете отключить и снова подключить плату к компьютеру. В Thonny вы должны увидеть появление платы и подключение консоли REPL. Вы можете использовать следующий код, чтобы проверить загруженную версию прошивки: >>> import os >>> os.uname() (sysname='rp2', nodename='rp2', release='1.20.0', version='v1.20.0 on 2023-04-26 (GNU 12.1.0 MinSizeRel)', machine='Arduino Nano RP2040 Connect with RP2040') >>> Здесь мы видим версию прошивки 1.20.0, которая на момент написания статьи была последней доступной. Здесь также отображается дата загрузки прошивки и имя файла прошивки. Наконец, мы видим еще название платы. Другие платы RP2040 Также растет число поставщиков, включающих чип RP2040 в свои собственные платы MicroPython. Возможно, наиболее значимой альтернативой Pico
Платы MicroPython RP2040  103 и Arduino Nano RP2040 является Seeed Studio XIAO1 RP2040 (www.seeedstudio.com/XIAORP2040-v1-0-p-5026.html), предлагающая мощь экосистемы RP2040 в компактных габаритах. Размер самой платы – 21×17,5 мм, что составляет небольшую часть размера других плат и делает ее отличным выбором для установок в ограниченном пространстве, например для носимых устройств. На рис. 3.24 показана Seeed Studio XIAO RP2040. Обзор оборудования Seeed Studio XIAO RP2040 – крошечная плата, Рис. 3.24  Seeed Studio XIAO лишенная многих функций (компонентов) боRP2040 (с разрешения сайта лее крупных плат2. Наиболее существенным seeedstudio.com) отличием является небольшое количество выводов GPIO. XIAO имеет следующие контакты и функции:         2 кнопки; 11 цифровых выводов; 4 аналоговых вывода; интерфейс I2C; порт UART; SPI-порт; интерфейс отладки SWD; RGB-светодиод. Как видите, это жизнеспособная альтернатива другим платам RP2040. Возможно, самое большое отличие заключается в том, что XIAO использует кабель USB-C, а не кабель micro-USB. Начало работы Вы можете запрограммировать Seeed Studio XIAO RP2040 с помощью Thonny после установки прошивки MicroPython. На новые платы прошивка не загружена, поэтому перед ее использованием вам необходимо будет ее установить. Как и Pico, вы можете перевести плату в режим USB-накопителя, который позволяет копировать файлы и загружать прошивку. Однако процесс немного отличается от Pico. 1 2 Произносится (грубо) «Ше-оу» и является названием китайской флейты. – Прим. авт. Хотя Raspberry Pi Pico все равно остается небольшой платой. XIAO крошечная по сравнению с ней. – Прим. авт.
104  Аппаратные средства MicroPython Загрузка прошивки Чтобы установить прошивку на Seeed Studio XIAO RP2040, вы можете следовать тем же инструкциям, которые мы использовали для загрузки прошивки на Raspberry Pi Pico/Pico W. Seeed Studio описывает процесс в своей документации (https://wiki.seeedstudio.com/XIAO-RP2040-with-MicroPython). Платы, совместимые с MicroPython С MicroPython можно использовать множество других плат, в которых не используется чип RP2040. В этом разделе кратко перечислены некоторые из наиболее популярных альтернатив для работы с MicroPython. Проекты в данной книге сосредоточены на Raspberry Pi Pico и Arduino Nano RP2040 Connect, но если у вас есть одна или несколько из этих альтернатив, вы также сможете использовать их в своих будущих проектах MicroPython. BBC micro:bit Плата BBC micro:bit специально разработана в расчете на простоту использования. Фактически она предназначена для того, чтобы помочь детям больше узнать об аппаратном и программном обеспечении. В этом отношении BBC micro:bit имеет успех. Ее размер составляет всего примерно 52×42 мм с компонентами на обеих сторонах. На одной стороне находится массив программируемых светодиодов и две кнопки. На другой стороне расположены компоненты, включая процессор, разъем micro-USB, кнопку сброса и разъем аккумулятора. Плата имеет выводы GPIO, расположенные вдоль нижнего края по обеим сторонам. Это упрощает использование платы с краевым разъемом. Контактные площадки с большими отверстиями (для подключения зажимов «крокодил») расположены на равном расстоянии друг от друга и предназначены для подключения общего провода (GND), питания (3 V) и трех выводов GPIO. На рис. 3.25 показаны лицевая и оборотная стороны основной платы BBC micro:bit. В новых платах добавляются дополнительные датчики и радиомодуль для подключения к другим платам. Еще одна причина успеха этой платы – программное обеспечение, созданное для ее поддержки. Разработчики создали удобное программное обеспечение для работы с платой. Поскольку плата предназначена для использования в качестве кода Arduino на C-подобном языке, доступное программное обеспечение выходит за рамки этой книги, но вы можете прочитать о нем больше на http://microbit. org. Однако имеется специальное приложение, которое позволит нам создавать, редактировать и запускать MicroPython на BBC micro:bit. Отлично!
Платы, совместимые с MicroPython  105 Рис. 3.25  Плата BBC micro:bit (спереди и сзади) Обзор оборудования Оригинальный BBC micro:bit не имеет модулей подключения к сети, но имеет Bluetooth, который можно использовать для подключения к другому устройству, пересылающему данные в интернет. В более новых платах BBC micro:bit добавлен радиоинтерфейс, позволяющий общаться с другими платами micro:bit. Его можно использовать для проектов интернета вещей, но не так просто, как Pico, и может потребоваться промежуточный узел, например ПК или небольшой компьютер вроде Raspberry Pi или даже обычного Arduino. Ниже приводится обзор некоторых аппаратных функций платы BBC micro:bit. Вы также можете получить доступ к встроенному USB-накопителю, когда плата подключена к компьютеру через USB-кабель.                 32-битный процессор ARM Cortex; 16 Кбайт ОЗУ; компас для определения ориентации; датчик температуры; динамик (новые платы); микрофон и светодиод (новые платы); красный индикатор питания (новые платы); желтый светодиод USB (новые платы); сенсорная площадка (новые платы); акселерометр для определения изменений в движении; Bluetooth с низким энергопотреблением (BLE); две программируемые кнопки; массив программируемых светодиодов 5×5; три цифровых/аналоговых вывода под зажим типа «крокодил»; 20 выводов GPIO на краевом разъеме; разъем для батареи. Теперь давайте посмотрим, как использовать плату с MicroPython.
106  Аппаратные средства MicroPython Начало работы Плата BBC micro:bit – это самая простая альтернатива для использования с MicroPython. Такое применение связано с двумя программными приложениями – приложением Mu (https://codewith.mu/en/download) и инструментом командной строки uFlash (https://uflash.readthedocs.io/en/latest). Mu – это полноценный редактор, который вы можете использовать на своем ПК и при подключении к BBC micro:bit через USB-кабель сохранять и выполнять сценарии. Инструмент uFlash можно использовать для переноса скриптов Python на плату вручную. Оба варианта доступны для применения в Windows, macOS и Linux. На рис. 3.26 показан пример использования Mu при написании короткого сценария MicroPython для вывода сообщения. Вы по-прежнему можете получить доступ к плате с помощью консоли REPL через приложение Mu, нажав кнопку REPL. Рис. 3.26  Редактор Mu для MicroPython на BBC micro:bit В отличие от других плат, на которых для использования MicroPython необходимо сначала установить прошивку, BBC micro:bit можно использовать для запуска сценариев MicroPython с помощью одного из этих инструментов. Например, мы можем использовать Mu для написания нашего сценария MicroPython, а затем «прошить» плату BBC micro:bit скомпилированной версией сценария (полученным бинарным файлом). Да, это означает, что вы можете написать свой скрипт, скомпилировать его и прошить (загрузить
Платы, совместимые с MicroPython  107 файл в шестнадцатеричных кодах) прямо на плату! Мы пишем код в редакторе и нажимаем Flash, чтобы загрузить его на плату. Самое приятное – то, что плата запрограммирована на запуск сценария при каждой загрузке. Это означает, что мы можем загрузить наш собственный код прямо на плату без дополнительной работы. Отлично! СОВЕТ Последние новости об использовании MicroPython на BBC micro:bit можно прочитать по адресу https://microbit-micropython.readthedocs.io/en/latest. Circuit Playground Express Следующая простая альтернативная плата – плата Circuit Playground Express от Adafruit. Circuit Playground Express – очень любопытная плата. Она создана по образцу нескольких других мобильных плат Adafruit, спроектированных таким образом, что их можно встроить в одежду. Плата круглая диамет­ ром около 50 мм. На внешней стороне платы расположены десять площадок с большими отверстиями (под разъемы типа «крокодил»), которые можно использовать с зажимами «крокодил» или для пришивания проводящей нитью. Adafruit также продает зажимы, которые могут оказаться необходимыми для использования этой платы с макетной платой (см. комплект «Small Alligator Clip» со штыревыми перемычками на сайте www.adafruit.com/product/3255). Еще одна интересная особенность – наличие десяти RGB NeoPixels – ярРис. 3.27  Circuit Playground Express ких светодиодов, цвет которых можно (с разрешения adafruit.com) менять с помощью кода. Плата также оснащена рядом датчиков, что делает ее подходящей альтернативой для экспериментов с проектами интернета вещей. На рис. 3.27 показана плата Circuit Playground Express. Обзор оборудования Ниже приведен обзор некоторых аппаратных функций платы Circuit Playground Express. Вы также можете получить доступ к встроенному USBнакопителю, когда плата подключена к компьютеру через USB-кабель:   процессор ATSAMD21 ARM Cortex M0, работающий при напряжении 3,3 В и частоте 48 МГц;
108  Аппаратные средства MicroPython   2 Мбайта флеш-памяти SPI, используемой в основном с CircuitPython для хранения кода и библиотек;   порт micro-USB для программирования и отладки. Порт USB может действовать как последовательный порт, клавиатура, мышь, джойстик или MIDI;   10 мини-светодиодов RGB NeoPixel;   датчик движения;   датчик температуры (термистор);   инфракрасный приемник и передатчик;   датчик освещенности (фототранзистор). Также может выступать в качестве датчика цвета и датчика пульса;   датчик звука (МЭМС-микрофон);   мини-динамик;   две кнопки;   ползунковый переключатель;   инфракрасный приемник и передатчик. Также может выступать в качестве датчика приближения;   8 входных/выходных выводов для зажимов типа «крокодил»;   I2C, UART, 8 контактов для аналоговых входов, несколько выходов ШИМ;   7 площадок могут действовать как емкостные сенсорные входы, и еще одна является настоящим аналоговым выходом;   красный программируемый светодиод;   кнопка сброса. Circuit Playground Express не имеет сетевых возможностей, для подключения к сети необходимо использовать внешний модуль. Лучший вариант, который я нашел, – это модуль Bluetooth, но альтернативой могут быть и другие модули, такие как плата расширения CC30001. Плата также не поддерживает MicroPython. Вместо этого на ней запускается специальная версия MicroPython под названием CircuitPython. CircuitPython – это производная версия MicroPython, разработанная и поддерживаемая Adafruit, созданная специально для Circuit Playground Express и ряда других плат, включая Circuit Playground Express, Feather и другие популярные платы. Хотя CircuitPython совместим с MicroPython, для некоторых функций вам может потребоваться использовать другие аппаратные библиотеки. Есть некоторые различия, описанные в документации по адресу https://github.com/ adafruit/circuitpython#differences-from-micropython. Но для наших целей он будет работать почти так же, как мы ожидаем от MicroPython. У Adafruit есть отличный набор руководств и блогов, которые помогут вам в освоении плат. Дополнительную информацию о CircuitPython см. по адресам: https://github.com/adafruit/circuitpython и https://learn.adafruit.com/ search?q=circuitpython&. 1 CC3000 – плата расширения для Arduino с SPI-интерфейсом, осуществляющая подключение к сети Wi-Fi. – Прим. перев.
Платы, совместимые с MicroPython  109 Теперь, когда мы провели краткий обзор аппаратного обеспечения, давайте посмотрим, как использовать плату с MicroPython. Начало работы Хотя на плате Circuit Playground Express нет такого необычного приложения, как у BBC micro:bit, после установки правильного драйвера загрузка двоичных файлов CircuitPython на плату становится на удивление простой. Фактически все, что вам нужно сделать, – это загрузить текущий бинарный файл и скопировать его на диск платы. Необходимые шаги следующие. ПРИМЕЧАНИЕ Если вы используете Windows, вам может потребоваться загрузить драйвер Adafruit по адресу: https://github.com/adafruit/Adafruit_Windows_Drivers/releases/download/1.0.0.0/adafruit_drivers.exe. Просто скачайте и запустите установщик. Драйвер поддерживает многие платы Adafruit. На странице параметров установки вы можете выбрать, какие платы вы хотите поддерживать. Я рекомендую выбрать все платы, чтобы вы могли использовать любую из плат Adafruit, поддерживающих CircuitPython. Как видите, таких вариантов множество. На рис. 3.28 показан пример варианта установки. Рис. 3.28  Драйвер платы Adafruit – варианты установки Далее нам нужно скачать фирменную прошивку. Adafruit создала прошивку в специальном формате под названием USB Flashing Format (UF2). Последняя версия доступна по адресу https://github.com/adafruit/circuitpython/ releases. После загрузки прошивки вы можете подключить Circuit Playground Express к компьютеру, а затем дважды нажать кнопку сброса (расположенную в цент­ ре платы). Когда USB-накопитель будет подключен (он должен называться CIRCUITBOOT), скопируйте с него старый файл UF2 с именем CURRENT.UF2 на свой компьютер. Мы будем использовать его в качестве резервной копии.
110  Аппаратные средства MicroPython Затем перетащите новый UF2 в папку CIRCUTPY. Когда копирование файла будет завершено, плата перезагрузится и запустит CircuitPython. Как видите, это намного проще, чем во многих других платах. Теперь в Thonny вы должны увидеть плату или иметь возможность ее выбрать с помощью кнопки в правом нижнем углу окна. На рис. 3.29 показана консоль REPL с подключенной Circuit Playground Express. Рис. 3.29  Консоль REPL в Windows (Circuit Playground Express) А что насчет других плат? По мере роста популярности MicroPython вы, вероятно, увидите больше доступных плат. Фактически в настоящее время предпринимаются попытки сделать MicroPython доступным на нескольких платах, включая Teensy, Arduino, и некоторых вариантах на основе набора микросхем ESP8266/ESP32 (Espressif). Доступны некоторые ранние ограниченные версии, например для плат версии Teensy 3.х, которые можно загрузить с помощью MicroPython, но для этого требуется опыт кросс-компиляции, и, следовательно, такой путь не для новичков (можете попробовать!). Однако имейте в виду, что сторонние платы могут несколько отставать по возможностям, а документация обычно в лучшем случае неполная. Но теперь, когда вы прочитали о серийных платах, у вас должны быть знания, необходимые для работы с новыми платами. Если плата, которую вы хотите использовать, не описана в этой главе, вы можете следить за веб-сайтом MicroPython (http://micropython.org/download) для получения самой последней информации о наличии установленного встроенного ПО MicroPython для вашей платы.
Выносные модули и дополнительные компоненты  111 Теперь, когда мы рассмотрели несколько доступных плат MicroPython, давайте углубимся в аксессуары, которые вы можете использовать для создания своих проектов. Выносные модули и дополнительные компоненты Последнее оборудование, которое мы рассмотрим, – это платы, доступные для использования с платой MicroPython. Это могут быть специальные отдельные модули (называемые выносными модулями или подключаемыми платами) или платы, специально разработанные для использования с платой MicroPython (в зависимости от платы называемые платами расширений или оболочками). В следующих разделах кратко описаны некоторые выносные модули, платы расширений/оболочки и некоторые аксессуары, которые вы можете рассмотреть в зависимости от того, какую плату вы выберете. Список не является исчерпывающим и не является списком вещей, которые вам необходимо купить. Мы увидим рекомендуемое оборудование в каждой из глав примеров после обсуждения MicroPython. Выносные модули Выносные модули (breakout boards) – один из ключевых элементов, которые любители и энтузиасты будут использовать при создании IoT-решений на MicroPython (или любой другой платформе). Это связано с тем, что выносные модули представляют собой небольшие печатные платы, которые содержат все компоненты, необходимые для поддержки таких функций, как датчик, двигатель, сетевой интерфейс или даже дисплей. Выносные модули обычно также поддерживают один из нескольких протоколов связи, для которых требуется подключение всего нескольких контактов, что делает их простыми в использовании. В общем, они экономят разработчику много времени, когда он пытается понять, как спроектировать схемы для поддержки датчика или какой-то микросхемы. Чтобы использовать выносные модули в наших проектах, нам нужно только знать, какой интерфейс использовать и как его подключить. К счастью, большинство поставщиков предоставляют техническое описание и другую документацию, которая поможет подключить модуль к вашей плате. Даже если у поставщика есть документация только для Arduino, все равно возможно научиться выполнять подключения. Хитрость заключается в том, чтобы научиться писать код MicroPython. Мы узнаем больше о библиотеках MicroPython и аппаратной поддержке в главах 5 и 6. А пока давайте рассмотрим некоторые из доступных выносных модулей. Опять же, то, что показано здесь, является лишь очень небольшой выборкой.
112  Аппаратные средства MicroPython Огромный выбор выносных модулей имеется у Adafruit. Вы можете увидеть их продукцию на www.adafruit.com/category/42. У любого поставщика есть платы, которые хорошо работают и имеют достаточную документацию для их поддержки. Но, как уже отмечалось, вам может потребоваться адаптировать их для использования с аппаратными библиотеками MicroPython. Следующие примеры плат аналогичны тем, которые мы будем использовать в последующих главах для чтения данных о погоде. Существует множество модулей, оснащенных метеодатчиками. Наиболее распространенными и простыми в использовании являются те, которые считывают температуру, влажность и атмосферное давление. На рис. 3.30 показан пример двух таких модулей: один от Adafruit, а другой от SparkFun. Adafruit MPL115A2 измеряет атмосферное давление и температуру, а SparkFun BME 280 измеряет атмо­ сферное давление, влажность и температуру. Оба используют интерфейс I2C. Рис. 3.30  Модули с метеодатчиками (с разрешения adafruit.com и sparkfun.com) Следующий выносной модуль сам по себе часто считается датчиком, поскольку он выполняет только одну функцию, но это все равно модуль. Многие датчики упаковываются таким образом и часто называются датчиками, когда они представляют собой модули. К счастью, большинство людей поймут, что вы имеете в виду, если вы употребите неправильный термин. Вы можете определить, является ли это выносным модулем, если плата, на которой установлен датчик, содержит другие дискретные компоненты и контакты разъема. В данном случае модуль измеряет влагосодержание в почве и может оказаться полезным при создании решения для мониторинга состояния растений, как мы это сделаем в следующей главе. На рис. 3.31 показана плата измерения влагосодержания почвы от SparkFun (www.sparkfun.com/products/13322). Обратите внимание на уникальную форму платы. Два длинных штыря являются частью сенсорного устройства, измеряющего влажность. Вверху находится набор контактов с отверстиями (разъемы не установлены), которые используются для подключения к контроллеру. Этот выносной модуль не использует специальный интерфейс; датчик просто вырабатывает напря-
Выносные модули и дополнительные компоненты  113 жение, которое вы можете измерить одним из аналоговых выводов платы MicroPython. Фактически все, что нам нужно, – это три провода: питание 5 V, общий (GND) и выходной сигнал (который подключается к аналоговому выводу на нашей плате). Опять же, в следующей главе мы увидим, как использовать этот модуль. Рис. 3.31  Датчик влагосодержания почвы (с разрешения sparkfun.com) Последний модуль – это специальный аксессуар для платы BBC micro:bit. Это краевой разъем, который можно использовать для подключения платы BBC micro:bit к макетной плате, что упрощает подключение к другим компонентам. Этот модуль можно найти на сайте SparkFun (www.sparkfun.com/products/13989). Я настоятельно рекомендую данный или аналогичный вариант, если вы решите использовать плату BBC micro:bit для запуска MicroPython. На рис. 3.32 показана коммутационный модуль Edge для платы BBC micro:bit от SparkFun. Рис. 3.32  Плата Edge Breakout Board для BBC micro:bit (с разрешения sparkfun.com)
114  Аппаратные средства MicroPython Специализированные платы расширения Поставщики плат MicroPython, а также поставщики широкого спектра микроконтроллеров и подобных плат упаковывают свои компоненты так, чтобы их можно было использовать с платами расширения (шилдами), платами-оболочками или чем-то подобным. Например, дополнительные платы BeagleBone называются накидками (capes), а дополнительные платы Raspberry Pi – шляпами (hats). Это платы с соответствующими разъемами, которые используются для установки поверх основной платы MicroPython для добавления функциональности. Многие платы расширения и оболочки имеют сквозные разъемы, которые позволяют добавлять более одной платы одновременно. Например, формат плат расширения Arduino можно настроить с добавлением двух, трех или более шилдов. Аналогичным образом Arduino Nano RP2040 Connect имеет несколько компонентов, которые можно использовать с платой, в том числе изящный адаптер с винтовыми клеммами Nano Screw Terminal Adapter (https://store.arduino.cc/products/nano-screw-terminal), в разъемы которого устанавливается Nano и каждый контакт имеет винтовую клемму, позволяющую выполнять надежные соединения с платой. На рис. 3.33 показан адаптер с винтовыми клеммами. Рис. 3.33  Адаптер Nano Screw Terminal Adapter (с разрешения arduino.cc) Обратитесь к поставщику платы, чтобы узнать, какие специальные расширения или оболочки доступны для выбранной вами платы.
Выносные модули и дополнительные компоненты  115 Аксессуары для конкретных плат контроллеров Последняя категория оборудования, которую мы обсудим, – это аксессуары, доступные для некоторых плат, обсуждаемых в данной главе. Как и почти все в жизни, мы можем оснастить наши платы аксессуарами для серьезных (и дорогостоящих) целей, и да, вы можете достичь предельной навороченности Raspberry Pi или Arduino. В реальности для Pico и micro:bit есть несколько аксессуаров. Это не значит, что вам следует бежать и покупать их или что они необходимы для этой книги; скорее, я думаю, вам следует рассмотреть их, если вы планируете продолжать разработку с этими платами, после того как выполните примеры в нашей книге. Макетная плата для Raspberry Pi Pico/Pico W Лучшим аксессуаром для Pico является макетная плата, доступная на ThePiHut.com (https://thepihut.com/products/breadboard-for-pico). Это обычная контактная макетная плата половинного размера, которая работает так же, как и любая другая макетная плата, но на ее поверхности напечатаны контакты для Pico, что делает ее полезным аксессуаром при построении схем с по­ мощью Pico. На рис. 3.34 показана такая макетная плата с установленным Pico. Я рекомендую эту плату, если вы планируете использовать Pico в проектах, описанных в этой книге. Рис. 3.34  Макетная плата для Pico (с разрешения thepihut.com)
116  Аппаратные средства MicroPython Аксессуары для BBC micro:bit Плата с самым впечатляющим списком доступных аксессуаров – это BBC micro:bit. Я уверен, что это связано с его популярностью, но это очень полезное свойство. Вы можете найти наборы для сборки роботов, электроаксессуары, кейсы и многое другое! Один из вариантов питания, доступных для BBC micro:bit, который мне нравится, – это плата MI:power от Kitronik (www.kitronik.co.uk/5610-mipower-board-for-the-bbc-microbit.html). Эта плата хороша тем, что она крепится к плате BBC micro:bit с помощью больших контактов типа «крокодил» (0, 1, 2, 3V и GND), используя для питания только одну небольшую батарейку типа «монетка» (CR2032) на 3 В. При установке не занимает много места. Плата имеет выключатель, позволяющий выключить плату, и небольшой динамик (подключение через контакт № 0), поэтому вы можете добавить звук в свой проект. На рис. 3.35 показана плата MI:power от Kitronik. Рис. 3.35  Плата MI:Power для BBC micro:bit Если вы решите использовать эту плату для питания (а я рекомендую ее для BBC micro:bit вместо внешней батареи) и решите установить все в корпус, вы должны знать, что большинство корпусов не предназначены для поддержки платы MI:power. Тем не менее я нашел по крайней мере один. Неудивительно, что его выпускают те же люди, что и MI:power! Для BBC micro:bit доступно множество корпусов, и, похоже, каждый день появляются новые (включая те конструкции, которые можно распечатать на 3D-принтере). Однако тот, который мне нравится, похож на те, что я использовал для других плат. Он изготовлен из акрила и использует нейлоновые болты для соединения нескольких слоев. Это чистая сборка, позволяющая четко видеть плату, что приятно, учитывая, что BBC micro:bit имеет много светодиодов! На рис. 3.36 показан вариант корпуса MI:pro для BBC micro:bit.
Какую плату приобрести  117 Рис. 3.36  Комплект корпуса MI:pro для BBC micro:bit Наконец, если вы собираетесь часто использовать BBC micro:bit для проведения экспериментов, включая примеры из этой книги, а также десятки других примеров, доступных в интернете, вам следует рассмотреть комплект прототипирования от Kitronik (www.kitronik.co.uk/5609-prototyping-system-forthe-bbc-microbit.html). В этот комплект входит плата, на которую можно установить входящие в комплект краевой разъем и обычную макетную плату, что делает макет красивым и аккуратным. На рис. 3.37 показан комплект прототипирования от компании Kitronik. Этот комплект включен в более крупный «комплект изобретателя», описанный в следующей врезке. Теперь давайте кратко обсудим, какую плату вам следует купить для использования в MicroPython для развития интернета вещей. Какую плату приобрести? Итак, вы хотите купить плату MicroPython, но не знаете, какую выбрать. К счастью, все платы из этой главы могут стать идеальным выбором. Характеристики, которые могут сделать иные из них более подходящими для некоторых применений, включают следующее:   не требуется доработка. Если вы не умеете паять или не хотите тратить время на загрузку программного обеспечения, загрузку и прошивку, вам следует рассмотреть готовую к работе плату, например Pico или Arduino Nano. Любую из них можно приобрести с установленными разъемами;
118  Аппаратные средства MicroPython Рис. 3.37  Система прототипирования BBC micro:bit (с разрешения kitronik.uk.co)   возможности подключения. Если вы планируете сделать свое решение интернета вещей общедоступным (а не академическим упражнением), вам следует рассмотреть возможность использования плат со встроенным Wi-Fi или аналогичными сетевыми возможностями;   существующее оборудование. Если вы уже вложили деньги и у вас есть платформа и устройства (платы модулей, платы расширений и т. д.), вы можете рассмотреть возможность остаться с имеющейся платой. Например, если вы много вложили в Arduino, вам следует рассмотреть возможность загрузки MicroPython на плату Arduino. Некоторые платы Arduino совместимы с MicroPython. См. https://docs.arduino.cc/learn/ programming/arduino-and-python для получения полного списка совмес­ тимых плат Arduino. Выбор того, какую плату купить, требует некоторой предусмотрительности. Опять же, если вы настоящий энтузиаст, вы можете купить несколько таких плат и поэкспериментировать, чтобы определить, какая из них лучше всего подходит для вашего проекта. В любом случае, я считаю, что лучшими для изучения MicroPython в IoT-проектах являются Pico и Nano RP2040. Тем не менее платы XIAO и micro:bit также представляют собой отличные платы для изучения MicroPython, но требуют внешнего модуля для добавления сетевых возможностей.
Какую плату приобрести  119 Покупка комплекта Многие платы поставляются в комплекте с набором аксессуаров, в том числе набором для начала работы, в который входят плата и адаптер питания. Другие комплекты могут включать макеты и часто датчики или дополнительные платы. Например, вы можете приобрести несколько комплектов для платы BBC micro:bit, включая два симпатичных варианта от SparkFun. • BBC micro:bit Go Bundle: содержит плату и аккумулятор (www.sparkfun.com/products/14336); • набор изобретателя SparkFun для BBC micro:bit: версия чрезвычайно популярного набора изобретателя для BBC micro:bit, включающая все основные электронные компоненты, которые вам понадобятся как минимум для 14 различных экспериментов, а также легко читаемый буклет с инструкциями (www.sparkfun.com/ products/14300); • комплект micro:climate: содержит плату, дополнительную плату метеостанции, а также метеодатчики (www.sparkfun.com/products/14217). У других производителей могут быть дополнительные или аналогичные комплекты для других плат. Например, у Kitronik есть отличный «комплект изобретателя» для BBC micro:bit (показанный ниже – любезно предоставлено kitronik.co.uk), содержащий множество деталей, которые вам понадобятся. Если вы только начинаете и у вас нет плат или компонентов, набор для начала работы может оказаться наиболее экономичным вариантом.
120  Аппаратные средства MicroPython Итоги В этой главе вы получили много информации. Как видите, доступно несколько плат MicroPython. Некоторые из них, например Pico и Nano RP2040 Connect, готовы к использованию и не требуют ничего, кроме подключения, загрузки прошивки и написания первого скрипта Python. Для других может потребоваться дополнительная работа, чтобы понять, как их использовать. Однако все представленные здесь платы являются отличным выбором для экспериментов в этой книге. В данной главе мы рассмотрели некоторые лучшие практики и советы по использованию платы MicroPython. Мы рассмотрели обычные вещи, которые могут пойти не так, а также куда обратиться, чтобы найти решения других проблем, с которыми вы можете столкнуться. Наконец, мы рассмотрели несколько популярных расширений и дополнительных плат, которые можно использовать для разработки проектов, включая те, которые будут описаны далее в этой книге. В следующей главе мы углубимся в руководство по программированию с использованием Python и MicroPython. Глава представляет собой краткий обзор и призвана помочь вам дойти до момента, когда вы сможете написать (и понять) примеры в этой книге.
Глава 4 Как программировать на MicroPython Теперь, когда у нас есть базовое представление о доступных платах MicroPython, мы можем узнать больше о программировании на MicroPython – надежном и простом языке, который можно использовать для написания очень мощных приложений. Освоить MicroPython легко, и некоторые могут предположить, что для его использования не требуется никакого формального обучения. Во многом это так, и поэтому вы сможете писать сценарии MicroPython, имея лишь небольшие знания этого языка. Учитывая, что MicroPython – это Python, мы можем сначала изучить основы языка Python на примерах на нашем ПК. Таким образом, эта глава представляет собой ускоренный курс по основам программирования на Python, включая объяснение некоторых наиболее часто используемых функций языка. Здесь будут предоставлены навыки, необходимые для понимания примеров проектов Python IoT, доступных в интернете. В главе также показано, как программировать на Python, на примерах, которые вы можете запустить на своем ПК или плате MicroPython. Итак, начнем! ПРИМЕЧАНИЕ В этой главе я использую термин Python для описания концепций программирования, применимых как к MicroPython, так и к Python. В концепциях, уникальных для MicroPython, используется термин MicroPython. Сначала давайте изучим некоторые основные концепции программирования на Python. Мы начнем со строительных блоков языка, таких как переменные, модули и базовые операторы, а затем перейдем к более сложным концепциям управления потоками и структурами данных. Хотя может показаться, что материал дается вам в спешке, в этом руководстве по Python рассматриваются только самые фундаментальные знания о языке и о том,
122  Как программировать на MicroPython как его использовать на вашем ПК и плате MicroPython. Он предназначен для того, чтобы вы могли начать писать приложения Python IoT. Если вы знаете основы программирования на Python, смело пролистывайте эту главу. Тем не менее я рекомендую поработать с примерами проектов в конце главы, особенно если вы написали не так много приложений на Python. В следующих разделах представлены многие основные особенности программирования на Python, которые вам необходимо знать, чтобы понять примеры проектов в этой книге. Базовые концепции Python – это интерпретируемый объектно ориентированный язык высокого уровня. Одна из главных целей Python – создать ясный и простой для понимания синтаксис, максимально приближенный к естественному английскому языку. То есть вы должны уметь читать скрипт Python и понимать его, даже если вы не изучали язык. В Python также меньше знаков препинания (специальных символов) и синтаксических конструкций, чем в других языках. Ниже перечислены некоторые ключевые особенности Python:   интерпретатор обрабатывает Python во время выполнения. Внешний (отдельный) компилятор не используется;   Python поддерживает конструкции объектно ориентированного программирования посредством классов;   Python – отличный язык для программистов начального уровня, поддерживающий разработку широкого спектра приложений;   Python – это язык сценариев, но его можно использовать для широкого спектра приложений;   Python очень популярен и используется во всем мире, что обеспечивает ему огромную базу поддержки;   в Python мало ключевых слов, простая структура и четко определенный синтаксис. Это позволяет начинающему быстро освоить язык;   код Python более четко определен и обозреваем. Python доступен практически для любой платформы, с которой вы можете столкнуться или использовать. Python – очень простой для изучения язык с очень небольшим количеством конструкций, которые хоть немного были бы сложны в изучении. Вместо того чтобы выбрасывать пример приложения, давайте подойдем к изучению основ Python аналогично сценарию Python: шаг за шагом. ПРИМЕЧАНИЕ Если у вас нет платы MicroPython и вы не установили Python на свой компьютер, вам следует сделать это сейчас, чтобы можно было запустить примеры из этой главы.
Базовые концепции  123 Блоки кода Первое, что вам следует усвоить, – это то, что Python не использует блок кода, разграниченный некими специальными символами (операторными скобками), как другие языки. Код, который является телом такой конструкции, как функция, условие или цикл, обозначается с помощью отступов. Например, следующие строки имеют отступ (с помощью пробелов или знаков табуляции1), чтобы начальные символы строк блока кода выравнивались одинаково. ВНИМАНИЕ! Ниже показана эта концепция в действии. Интерпретаторы Python будут жаловаться и могут давать странные результаты, если отступы неодинаковы. if (expr1): print("inside expr1") print("still inside expr1") else: print("inside else") print("still inside else") print("in outer level") Здесь мы видим условный оператор (оператор if). Обратите внимание, что вызов функции print() имеет отступ. Это сигнализирует интерпретатору, что строки принадлежат конструкции, в которую они включены. Например, два оператора вывода print(), в которых упоминается выражение expr1, образуют блок кода для условия if (и выполняются, когда выражение expr1 принимает значение true). Аналогично следующие два оператора вывода образуют блок кода для условия else. Наконец, строки без отступов не являются частью условия и поэтому выполняются после if или else в зависимости от значения выражения expr1. Как видите, отступы – ключевое понятие, которое необходимо изучить при написании Python. Несмотря на то что это очень просто, ошибки в отступах могут привести к результатам, которых вы не ожидали, или к еще более серь­ езным ошибкам со стороны интерпретатора. ПРИМЕЧАНИЕ При обсуждении Python я использую слова «программа» и «приложение» как синонимы «сценария». Хотя технически код Python, сохраненный в файле, является сценарием, мы часто используем его в контекстах, где более уместны слова «программа» или «приложение». Есть один специальный символ, с которым вы будете часто сталкиваться. Обратите внимание на использование двоеточия (:) в представленном коде. Этот символ используется для завершения объявления конструкции и сигнализирует интерпретатору о том, что за ним следует тело блока кода. Мы используем этот символ для условий, циклов, классов и функций. 1 Отступы с помощью знаков табуляции поддерживают не все интерпретаторы и среды программирования на Python. – Прим. перев.
124  Как программировать на MicroPython Комментарии Одной из наиболее фундаментальных концепций любого языка программирования является возможность пояснять исходный код неисполняемым текстом, что не только позволяет делать заметки в строках кода, но и формирует способ документирования исходного кода. Чтобы добавить комментарии к исходному коду, используйте знак решетки (#). Поместите хотя бы один в начале строки, чтобы создать комментарий для этой строки, повторяя символы # для каждой последующей строки. Это создает так называемый блочный комментарий, как показано ниже. Обратите внимание, что я использовал комментарий без текста, чтобы создать пустые строки. Это помогает улучшить читаемость и является обычной практикой для блочных комментариев: # # MicroPython for the IoT Second Edition # # Example Python application. # # Created by Dr. Charles Bell # Вы также можете размещать комментарии в той же строке, что и исходный код. Компилятор или интерпретатор будет игнорировать все, от знака решетки до конца строки1. Например, ниже показан общий стиль документирования переменных: zip = 35012 # Zip or postal code address1= "123 Main St." # Store the street address Арифметические и другие операторы В Python можно выполнять множество математических операций, включая обычные арифметические действия, а также логические операции и операции, используемые для сравнения значений. Вместо того чтобы обсуждать это подробно, я приведу краткую справку в табл. 4.1, в которой показаны операции и примеры их использования. Побитовые операции дают результат по значениям, выполненным для каждого бита. Логические операторы (and, or) создают логическое значение, которое является либо истинным (true), либо ложным (false), и часто используются с выражениями или условиями. Унарный плюс (+) возвращает операнд неизмененным, унарный минус (–) меняет знак операнда. 1 Из этого положения, в частности, следует, что комментарий может быть на любом языке. Мы этим воспользуемся далее для перевода некоторых комментариев, важных для понимания кода; код все равно можно будет загружать в любой интерпретатор Python. – Прим. перев.
Базовые концепции  125 Таблица 4.1. Арифметические, логические операторы и операторы сравнения в Python1 Тип Арифметический Логический Сравнение Оператор + – * / % + – & | ^ ~ and or == != < ~ <= >= Описание Сложение Вычитание Умножение Деление Остаток от деления Унарный плюс Унарный минус Побитовое И Побитовое ИЛИ Побитовое исключающее ИЛИ Побитовое НЕ Логическое И Логическое ИЛИ Равно Не равно Меньше Больше Меньше или равно Больше или равно Пример int_var + int_var int_var * int_var / 1 1 2 3 int_var % 4 +int_var -int_var var1 & var2 var1 | var2 var1 ^ var2 ~var1 var1 and var2 var1 or var2 expr1 == expr2 expr1 != expr2 expr1 < expr2 expr1 > expr2 expr1 <= expr2 expr1 >= expr2 Вывод на экран Мы уже видели несколько примеров вывода сообщений на экран, но без каких-либо объяснений показанных выражений. Хотя маловероятно, что вы будете выводить на экран выходные данные с вашей платы MicroPython для развертываемых проектов, изучение Python будет намного проще, если вы можете отображать сообщения на экране. Одна из вещей, которую вы, возможно, захотите вывести на экран, как мы видели в предыдущих примерах, – это сообщить о том, что происходит в самой программе. Сюда могут входить простые сообщения (строки), а также значения переменных, выражений и т. д. Как мы видели, встроенная функция print() является наиболее распространенным способом отображения текста, заключенного в одинарные или двойные кавычки. Также можно найти несколько интересных примеров использования функции с именем format(). Функция format() генерирует строку для каждого переданного аргумента. Этими аргументами могут быть дру1 В таблице приведен неполный перечень операторов Python. Более полный список, например, см. (на русском языке): https://pythonru.com/osnovy/operatory-python. – Прим. перев.
126  Как программировать на MicroPython гие строки, выражения, переменные и т. д. Функция используется со специальной строкой, которая содержит ключи замены, разделенные фигурными скобками { } (так называемая строковая интерполяция1). Каждый ключ замены содержит либо индекс (начиная с 0), либо именованное ключевое слово. Специальная строка называется строкой формата. Давайте посмотрим несколько примеров, иллюстрирующих эту концепцию. Вы можете запустить их самостоятельно либо на своем ПК, либо на плате MicroPython. Я включаю результаты выполнения (то, что должно отобразиться на экране), чтобы вы могли видеть, что делает каждый оператор: >>> a = 42 >>> b = 1.5 >>> c = "seventy" >>> print("{0} {1} {2} {3}".format(a,b,c,(2+3))) 42 1.5 seventy 5 >>> print("{a_var} {b_var} {c_var} {0}".format((3*3), c_var=c, b_var=b, a_var=a)) 42 1.5 seventy 9 Обратите внимание: я создал три переменные (о переменных мы поговорим в следующем разделе), присвоив им разные значения с помощью символа равенства (=). Затем я напечатал сообщение, используя строку формата с четырьмя ключами замены, помеченными индексом. Обратите внимание на результат этого оператора печати. Обратите внимание, что я включил выражение в конце, чтобы показать, как функция format() оценивает выражения. Последняя строка более интересна. Здесь я использовал три именованных параметра (a_var, b_var, c_var) и специальный параметр аргумента в функции format(), где присваиваю параметру значение. Обратите внимание, я перечислил их в другом порядке. Это самое большое преимущество использования именованных параметров; они могут появляться в любом порядке, но размещаются в строке формата в указанной позиции. Как видите, это всего лишь случай замены ключей { } на ключи из функции format(), которая преобразует аргументы в строки. Мы используем этот метод везде, где нам нужна строка, содержащая данные, собранные из более чем одной области или типа. Мы можем видеть это на предыдущих примерах. СОВЕТ См. https://docs.python.org/3/library/string.html#formatstrings для получения дополнительной информации о форматировании строк. Теперь давайте посмотрим, как мы можем использовать переменные в наших программах (сценариях). СОВЕТ Python позволяет завершать оператор точкой с запятой (;); однако его включение не является необходимым и считается дурным тоном2. 1 2 https://en.wikipedia.org/wiki/String_interpolation. – Прим. авт. Отсутствие явно обозначенного завершения оператора в Python может существенно снижать читаемость текста; особенно это касается длинных строк, которые приходится разбивать на части (тем более что строгие правила разбиения в Python отсутствуют). Для облегчения чтения таких текстов можно воспользоваться этой особен-
Базовые концепции  127 Переменные Python – это динамически типизированный язык, что означает, что тип переменной (тип данных, которые она может хранить) определяется контекстом по мере ее определения или использования. Это контрастирует с другими языками, такими как C и C++, где вы должны объявить тип перед использованием переменной. Переменные в Python – это просто именованные области памяти, которые вы можете использовать для хранения значений во время выполнения. Мы сохраняем значения, используя знак равенства для присвоения значения. Имена переменных Python могут быть любыми, но существуют правила и соглашения, которым следует большинство разработчиков Python. Правила перечислены в стандарте кодирования Python PEP8 (www.python.org/dev/peps/ pep-0008). Однако общее определяющее правило требует, чтобы имена переменных были описательными, имели значение в контексте и могли легко читаться. То есть вам следует избегать имен со случайными символами, вынужденных сокращений, выдуманных аббревиатур и подобных непонятных названий. По соглашению имена переменных должны быть длиннее одного символа (с некоторыми допустимыми исключениями для переменных подсчета циклов) и достаточно короткими, чтобы избежать слишком длинных строк кода. Что такое длинная строка кода? Большинство скажут, что строка кода не должна превышать 80 символов, но это отсылает к темным временам программирования, когда мы использовали перфокарты, допускавшие максимум 80 символов на карту, а также более поздние устройства отображения с тем же ограничением. На современных широкоэкранных дисплеях это не так уж важно, но я все же рекомендую делать строки короткими, чтобы обес­ печить лучшую читаемость. Никто не любит прокручивать вправо, чтобы увидеть строку до конца! Таким образом, существует большая гибкость в том, как вы можете называть свои переменные. В стандарте кодирования PEP8 есть дополнительные правила и рекомендации, и если вы хотите привести исходный код вашего проекта в соответствие со стандартами, вам следует просмотреть стандарт именования для функций, классов и т. д. Полный список правил и стандартов см. в руководстве PEP8 для кодирования Python по указанной ссылке. Ниже показаны некоторые примеры простых переменных и их динамически определяемых типов: # floating point number length = 10.0 ностью языка (правда, в этой книге вы таких примеров не встретите). – Прим. перев.
128  Как программировать на MicroPython # integer width = 4 # string box_label = "Tools" # list car_makers = ['Ford', 'Chevrolet', 'Dodge'] # tuple porsche_cars = ('911', 'Cayman', 'Boxster') # dictionary address = {"name": "Joe Smith", "Street": "123 Main", "City": "Anytown", "State": "New Happyville"} Итак, как мы узнали, что переменная width является целым числом? Просто потому, что значение 4, которое она принимает, – целое число. Аналогично Python интерпретирует «Tools» как строку. В следующем разделе мы узнаем больше о последних трех типах и других типах, поддерживаемых Python. СОВЕТ Дополнительную информацию о соглашениях об именах, регулируемых стандартом кодирования Python (PEP8), см. в разделе www.python.org/dev/peps/pep0008/#naming-conventions. Типы Как уже упоминалось, в Python нет формального механизма спецификации типов, как в других языках. Однако вы все равно можете определить переменные для хранения всего, что захотите. Фактически Python позволяет создавать и использовать переменные на основе контекста, и вы можете применять инициализацию, чтобы «установить» тип данных для переменной. Ниже показано несколько примеров: # Numbers float_value = 9.75 integer_value = 5 # Strings my_string = "He says, he's already got one." print("Floating number: {0}".format(float_value)) print("Integer number: {0}".format(integer_value)) print(my_string) Для ситуаций, когда вам необходимо преобразовать типы или вы хотите убедиться, что значения выводятся определенным образом, существует множество функций для преобразования типов. В табл. 4.2 показаны некоторые из наиболее часто используемых функций. Я расскажу о некоторых структурах данных подробнее в следующем разделе. Эти функции преобразования следует использовать с осторожностью, чтобы избежать потери или округления данных. Например, преобразование числа с плавающей точкой в целое число может привести к усечению. Аналогично вывод чисел с плавающей запятой на экран может привести к округлению.
Базовые структуры данных  129 Теперь давайте посмотрим на некоторые часто используемые структуры данных, включая эти странные вещи, называемые словарем (dictionary) и кортежем (tuple). Таблица 4.2. Преобразование типов в Python Функция int(x [,base]) long(x [,base]) float(x) str(x) tuple(t) list(l) set(s) dict(d) chr(x) hex(x) oct(x) Описание Преобразует x в целое число; base is optional (например, 16 для hex-числа) Преобразует x в длинное целое число Преобразует x в число с плавающей запятой Преобразует объект x в строку Преобразует t в кортеж Преобразует l в список Преобразует s во множество Создает словарь Преобразует целое в символ Преобразует целое в hex-строку Преобразует целое в восьмеричную строку Базовые структуры данных Того, что вы уже узнали о Python, достаточно для написания самых простых программ и более чем достаточно для реализации примеров проектов, приведенных ниже в этой главе. Однако когда у вас возникнет необходимость работать с данными, получаемыми либо от пользователя, либо от датчиков и подобных источников, вам понадобится способ их организовать и хранить, а также выполнять операции с данными в памяти. Ниже представлены три структуры данных в порядке сложности: списки, кортежи и словари. Списки Списки – это способ организации данных в Python. Это свободный способ создания коллекции. Элементы списков не обязательно должны иметь один и тот же тип данных. Списки также позволяют выполнять некоторые интересные операции, например добавлять элементы в конец, начало или по специальному индексу. Ниже показано, как создать список: # List my_list = ["abacab", 575, "rex, the wonder dog", 24, 5, 6] my_list.append("end") my_list.insert(0,"begin") for item in my_list: print("{0}".format(item))
130  Как программировать на MicroPython Здесь мы видим создание списка, для чего применяются квадратные скобки []. Элементы в определении списка разделяются запятыми. Обратите внимание, что вы можете создать пустой список, просто установив переменную, равную []. Поскольку списки, как и другие структуры данных, являются объектами, для списков доступно несколько операций, например следующие:          append(x): добавить x в конец списка; extend(l): добавить все элементы l в конец списка; insert(pos,item): вставить элемент в позицию pos; remove(value): удалить первый элемент, который соответствует значению value; pop([i]): удалить и вернуть элемент в позиции i или в конце списка; index(value): возвращает индекс первого элемента, который соответствует значению value; count(value): подсчитать вхождения значения value; sort(): сортировка списка (по возрастанию); verse(): обратная сортировка списка. Списки похожи на массивы в других языках и очень полезны для создания динамических коллекций данных. Кортежи Кортежи представляют собой более строгий тип коллекции. То есть они строятся на основе определенного набора данных и не допускают манипуляций, как список. Фактически вы не можете изменять элементы в кортеже. Таким образом, мы можем использовать кортежи для данных, которые не должны меняться. Ниже показан пример кортежа и способы его использования: # Tuple my_tuple = (0,1,2,3,4,5,6,7,8,"nine") for item in my_tuple: print("{0}".format(item)) if 7 in my_tuple: print("7 is in the list") Здесь мы видим создание кортежа, для чего используются круглые скобки (). Элементы в определении кортежа разделяются запятыми. Обратите внимание, что вы можете создать пустой кортеж, просто установив переменную, равную (). Поскольку кортежи, как и другие структуры данных, являются объектами, доступно несколько операций, таких как следующие:      x in t: определить, содержит ли кортеж t значение x; x not in t: определить, что значение x не содержится в кортеже t; s + t: объединить кортежи s и t; s[i]: получить элемент i; len(t): длина кортежа t (количество элементов);
Базовые структуры данных  131   min(t): минимальное (наименьшее) значение;   max(t): максимальное (наибольшее) значение. Если вы хотите еще больше структурировать хранение данных в памяти, то можете использовать специальную конструкцию (объект), называемую словарем. Словари Словарь – это структура данных, которая позволяет хранить пары ключ– значение, где данные оцениваются с помощью ключей. Словари – это очень структурированный способ работы с данными и наиболее логичная форма, которую мы будем использовать при сборе сложных данных. Ниже приведен пример словаря: # Dictionary my_dictionary = { 'first_name': "Chuck", 'last_name': "Bell", 'age': 36, 'my_ip': (192,168,1,225), 42: "What is the meaning of life?", } # Access the keys: print(my_dictionary.keys()) # Access the items (key, value) pairs print(my_dictionary.items()) # Access the values print(my_dictionary.values()) # Create a list of dictionaries my_addresses = [my_dictionary] Здесь очень многое происходит! Мы видим базовое объявление словаря, в котором для создания словаря используются фигурные скобки {}. Внутри них мы можем создать столько пар ключей и значений, сколько захотим, разделенных запятыми. Ключи определяются с использованием строк (по соглашению я использую одинарные кавычки, но двойные кавычки тоже подойдут) или целых чисел, а значения могут быть любым типом данных, который мы хотим. Например, для ключа my_ip мы используем кортеж. После создания словаря мы видим несколько операций, выполняемых с ним: печать ключей, печать всех значений и печать только значений. Ниже показаны результаты выполнения этого фрагмента кода в интерпретаторе Python: [42, 'first_name', 'last_name', 'age', 'my_ip'] [(42, 'what is the meaning of life?'), ('first_name', 'Chuck'), ('last_name', 'Bell'), ('age', 36), ('my_ip', (192, 168, 1, 225))] ['what is the meaning of life?', 'Chuck', 'Bell', 36, (192, 168, 1, 225)] '42': what is the meaning of life? 'first_name': Chuck
132  Как программировать на MicroPython 'last_name': Bell 'age': 36 'my_ip': (192, 168, 1, 225) Как мы видим в этом примере, для словарей доступно несколько операций (функций или методов), включая следующие:         len(d): количество элементов в словаре d; d[k]: элемент словаря d с ключом k; d[k] = x: назначить ключу k значение x; del d[k]: удалить элемент с ключом k; k in d: определить, есть ли в d элемент с ключом k; d.items(): возвращает список пар (ключ, значение) в словаре d; d.keys(): возвращает список ключей в словаре d; d.values(): возвращает список значений в словаре d. В совокупности этот список операций делает словари очень мощным инструментом программирования. Лучше всего то, что объекты можно размещать внутри других объектов. Например, вы можете создать список словарей, как я делал ранее, словарь, содержащий списки и кортежи, а также любую необходимую вам комбинацию. Таким образом, списки, кортежи и словари – это мощный способ управления данными в вашей программе. В следующем разделе мы узнаем, как мы можем контролировать ход выполнения наших программ. Операторы Теперь, когда мы знаем больше об основах Python, мы можем открыть для себя некоторые более сложные концепции кода, которые вам понадобятся для завершения проекта, такие как условные операторы и циклы. Условные операторы Мы уже встречали несколько простых условных операторов: т. е. операторов, предназначенных для изменения потока выполнения в зависимости от оценки одного или нескольких выражений. Условные операторы позволяют нам направлять выполнение наших программ по разным разделам (блокам) кода на основе оценки одного или нескольких выражений. Условный оператор в Python – это оператор if. Мы уже видели оператор if в действии на примерах кода. Обратите внимание, что его могут дополнять одно или несколько (необязательных) выражений else, которые выполняются, когда выражение для условия if оценивается как ложное. Мы можем объединить операторы if/else, чтобы охватить несколько условий, при этом выполняемый код зависит от оценки нескольких
Операторы  133 условий. Ниже показана общая структура оператора if, включая сокращенные внутренние условия else if (elif). Обратите внимание на объяснение в комментариях, как выполнение достигает тела каждого условия: if (expr1): # выполняется, только если expr1 истинно (true) elif ((expr2) or (expr3)): # выполняется, только если expr1 ложно (false) И либо expr2, либо expr3 истинно else: # выполняется, если оба набора условий оцениваются как ложные Хотя вы можете связывать операторы сколько угодно, будьте здесь осторожны, потому что чем больше у вас разделов elif, тем труднее будет понимать, поддерживать и избегать логических ошибок в ваших выражениях. Существует еще одна форма условного оператора, называемая тернарным оператором. Тернарные операторы более известны как условные выражения в Python. Эти операторы оценивают что-то на основе того, истинно условие или нет. Условные выражения – это сокращенное обозначение конст­рукции if-then-else, используемой (обычно) в операторе присваивания, как показано ниже: variable = value_if_true if condition else value_if_false Здесь мы видим, что если условие оценивается как true, используется значение, предшествующее if, но если условие оценивается как false, используется значение, следующее за else. Ниже приведен краткий пример: >>> numbers = [1,2,3,4] >>> for n in numbers: ... x = 'odd' if n % 2 else 'even' ... print("{0} is {1}.".format(n, x)) ... 1 is odd. 2 is even. 3 is odd. 4 is even. >>> Условные выражения позволяют вам быстро проверить условие вместо использования многострочного условного оператора, что может помочь сделать ваш код немного короче и легче для чтения. Циклы Циклы используются для управления повторяющимся выполнением блока кода. Существует три формы циклов, которые имеют немного разное поведение. Все циклы используют условные операторы, чтобы определить, следует повторять выполнение или нет. То есть они повторяются до тех пор, пока условие истинно. Я объясню каждый пример.
134  Как программировать на MicroPython Цикл while Условие цикла while находится в начале блока кода. Таким образом, циклы while выполняют тело «до тех пор, пока» условие оценивается как истинное. Ниже показан синтаксис цикла while. Эту форму цикла лучше всего использовать, когда вам нужно выполнить код только в том случае, если некоторые выражения оцениваются как истинные, например при итерации по коллекции вещей, количество элементов которых неизвестно (цикл до тех пор, пока в коллекции не закончатся элементы): while (expression): # do something here Цикл for Цикл for иногда называют счетным циклом из-за его уникальной формы. Циклы for позволяют вам определить счетную переменную и диапазон или список для перебора. Ниже показана структура цикла for. Эту форму цикла удобнее всего использовать для выполнения операции над коллекцией. В этом случае Python будет автоматически помещать каждый элемент коллекции в переменную для каждого прохода цикла до тех пор, пока не закончатся доступные элементы. for variable_name in list: # do something here Цикл range или счетный цикл Вы также можете выполнять циклы range, называемые счетными. Они используют специальную функцию range(), которая принимает до трех параметров: range([start], stop[, step]), где start – начальный номер (целое число), stop – последнее число в серии и step – приращение на каждом шаге. То есть вы можете считать на 1, 2, 3 и т. д. в диапазоне чисел. Ниже показан простой пример: for i in range(2,9): # do something here Существуют и другие варианты использования range(), с которыми вы можете столкнуться. Дополнительную информацию см. в документации по этой функции и другим встроенным функциям по адресу https://docs.python. org/3/library/functions.html. Python также предоставляет механизм управления ходом циклов (например, прерывание выполнения) с использованием нескольких специальных ключевых слов, а именно:   break: немедленный выход из тела цикла;   continue: перейти к следующей итерации цикла;   else: выполнить код по завершении цикла (не выполняется, если цикл был остановлен оператором прерывания).
Модульность: модули, функции и классы  135 Эти ключевые слова можно использовать по-разному, в частности для прерывания, но это не предпочтительный метод завершения и управления циклами. То есть профессионалы считают, что код условного выражения или обработки ошибок должен вести себя достаточно хорошо, чтобы не нуждаться в этих опциях. Модульность: модули, функции и классы Эта группа рассматривает наиболее продвинутые средства языка и включает модульность (т. е. организацию кода). Как мы увидим, мы можем использовать функции для группировки кода, устранения дублирования и внедрения (инкапсуляции) функциональности в объекты. Включаемые модули Приложения Python можно создавать из повторно используемых библиотек, предоставляемых средой Python. Они также могут быть созданы из пользовательских модулей или библиотек, которые вы создаете сами или загружаете у третьих лиц. Библиотеки или модули часто распространяются как набор файлов кода Python (например, файлы с расширением .py). Когда мы хотим использовать библиотеку (функцию, класс и т. д.), включенную в модуль, мы используем ключевое слово import и указываем имя модуля. Ниже приведены некоторые примеры: import os import sys Эти две строки демонстрируют, как импортировать базовый или общий модуль, предоставляемый Python. В этом случае мы используем или импортируем модули os и sys (операционная система и системные функции Python). СОВЕТ Обычно (но необязательно) импортируемые файлы перечисляются в следующем порядке: сначала встроенные модули, затем сторонние модули и, наконец, ваши собственные модули. Функции Python позволяет вам использовать модульность в вашем коде. Хотя он поддерживает объектно ориентированное программирование посредством классов (более продвинутая функция, с которой мы познакомимся далее),
136  Как программировать на MicroPython на более фундаментальном уровне вы можете разбить свой код на более мелкие фрагменты с помощью функций. Функции используют специальное ключевое слово для определения функции. Мы указываем def, за которым следует имя функции и список парамет­ ров, разделенных запятыми, в круглых скобках. Двоеточие используется для завершения объявления. Ниже приведен пример: def print_dictionary(the_dictionary): for key, value in the_dictionary.items(): print("'{0}': {1}".format(key, value)) # define some data my_dictionary = { 'name': "Chuck", 'age': 44, } Интересно, что делает этот странный код. Обратите внимание, что цикл for присваивает два значения из результата функции items(). Это специальная функция, доступная для объекта dictionary 1. Функция items() возвращает пары ключ–значение: отсюда и имена переменных key, value. Следующая строка выводит значения. Использование форматирования строк, в которых фигурные скобки определяют номер параметра, начиная с нуля, является обычным для приложений Python 3. См. https://docs.python.org/3/library/string.html#format-string-syntax для получения дополнительной информации о форматировании строк. Обратите внимание, что тело функции имеет отступ. Все операторы, находящиеся под этим объявлением функции, принадлежат функции и выполняются при вызове функции. Мы можем вызывать функции по имени, предоставляя любые параметры следующим образом (обратите внимание, как я ссылался на значения в словаре, используя имена ключей): print_dictionary(my_dictionary) print(my_dictionary['age']) print(my_dictionary['name']) Этот пример вместе с предыдущим кодом при выполнении генерирует следующее: >>> def print_dictionary(the_dictionary): ... for key, value in the_dictionary.items(): ... print("'{0}': {1}".format(key, value)) ... >>> # define some data ... my_dictionary = { ... 'name': "Chuck", ... 'age': 41, 1 Да, словари – это объекты! То же самое можно сказать и о кортежах, списках и многих других структурах данных. – Прим. авт.
Модульность: модули, функции и классы  137 ... } >>> print_dictionary(my_dictionary) 'name': Chuck 'age': 41 >>> print(my_dictionary['age']) 41 >>> print(my_dictionary['name']) Chuck Теперь давайте рассмотрим самое сложное понятие Python – объектно ориентированное программирование. Классы и объекты Возможно, вы слышали, что Python – это объектно ориентированный язык программирования. Но что это значит? Проще говоря, Python – это язык программирования, который предоставляет средства для описания объектов (некоторых вещей) и того, что вы можете с ними делать (операций, методов). Объекты – это расширенная форма абстракции данных, при которой данные скрыты от вызывающего объекта и управляются им только с помощью операций (методов), предоставляемых объектом. Как и в случае с любой технологией или концепцией, существует определенное количество терминов, которые вы должны выучить, чтобы иметь возможность понимать эту технологию и общаться с другими людьми. Ниже кратко описаны некоторые термины, которые вам необходимо знать, чтобы больше узнать об объектно ориентированном программировании:   Attribute (атрибут): элемент данных в классе;   Class (класс): конструкция кода, используемая для определения объекта в форме атрибутов (данных) и методов (функций), которые работают с данными. Доступ к методам и атрибутам в Python можно получить с помощью точечной записи;   Class instance variable (переменная экземпляра класса): переменная, которая используется для хранения экземпляра объекта. Они используются как любая другая переменная и в сочетании с точечной записью позволяют нам манипулировать объектами;   Instance (экземпляр): исполняемая форма класса, созданная путем присвоения класса переменной, инициализирующей код как объект;   Inheritance (наследование): включение атрибутов и методов из одного класса в другой;   Instantiation (создание экземпляра): создание экземпляра класса;   Method overloading (перегрузка метода): создание двух или более методов с одинаковым именем, но с отдельным набором параметров. Это позволяет нам создавать методы с одинаковыми именами, но может работать по-разному в зависимости от переданных параметров;   Polymorphism (полиморфизм): наследование атрибутов и методов базового класса с добавлением дополнительных методов или переопределением (изменением) методов.
138  Как программировать на MicroPython Существует еще множество терминов ООП, но именно с этими вы будете сталкиваться чаще всего. Синтаксис, применяемый нами в Python, – это методы классов, которые вы можете использовать, чтобы сделать ваши проекты модульными. Под модульностью мы подразумеваем, что исходный код устроен так, чтобы его было проще разрабатывать и поддерживать. Обычно мы размещаем классы в отдельных модулях (файлах кода), что помогает лучше организовать код. Хотя это и не обязательно, я рекомендую использовать данный метод для размещения каждого класса в его собственном исходном файле. Это упрощает внесение изменений или исправление проблем (ошибок)1. Итак, что такое классы Python? Начнем с рассмотрения этого понятия как приема структурной организации кода. Мы можем использовать класс для группировки данных и методов. Имя класса следует сразу за ключевым словом class, за которым следует двоеточие. Другие методы класса объявляются так же, как и любой иной метод, за исключением того, что первый аргумент должен быть self, что привязывает метод к экземпляру класса при выполнении. Я предпочитаю использовать термины, принятые разработчиками языка или сообществом разработчиков. Например, некоторые используют «функцию», а другие – «метод». Третьи могут использовать название «подпрограмма», «процедура» и т. д. Не имеет значения, какой термин вы используете, но вы должны стараться применять термины последовательно. Я использую термин «метод» при обсуждении объектно ориентированных примеров. То есть у класса есть методы, а не функции. Однако вы можете называть «метод» «функцией», и вы все равно будете правы (в основном2). 1 2 Не следует забывать, что чрезмерное увлечение структурированием программы на все более мелкие части с размещением их в отдельных модулях и файлах удобно для целей программирования, но затрудняет разбор программы, заставляя «прыгать» в поисках определений переменных и констант по куче файлов и модулей, часто размещенных в совершено разных местах (вы сможете убедиться в этом сами в последних главах книги). Поэтому структурирование, о котором пишет автор, уместно для программ, для которых планируется многократное использование в различных проектах с внесением некоторых изменений; но не следует увлекаться излишним разбиением программ, предназначенных для однократного применения в конкретном проекте, – вам же будет проще разбираться. – Прим. перев. Автор напрасно запутывает читателя в терминологии. Каждый из упомянутых им терминов имеет свое строго определенное значение в своей области: «подпрограмма» – это общее для любых языков программирования понятие, так называется блок кода, имеющий свое имя и выполняемый как единое целое; «процедура» – подпрограмма, выполняющая какие-то манипуляции над данными, но не возвращающая значений через свое имя; «функция» – подпрограмма, возвращающая значение определенного типа. В С-подобных языках и процедуры, и функции называются одинаково – функциями, процедуры при этом сопровождаются словом void вместо указания типа. «Метод» – это понятие относится только к ООП: так называется функция, которая привязана к определенному объекту. Наконец, у ООП-объектов, кроме методов, имеются еще свойства, называемые в Python «атрибутами». К счастью, автор более-менее следует этой общепринятой терминологии. – Прим. перев.
Модульность: модули, функции и классы  139 Доступ к данным осуществляется с помощью одного или нескольких методов, создавая экземпляр класса и используя точечную нотацию для ссылки на данные (атрибуты) или методы. Давайте посмотрим на пример. В листинге 4.1 показан полный класс, который описывает (моделирует) самые основные характеристики транспортного средства, используемого для перевозки. Я создал файл с именем Vehicle.py, содержащий этот код. Листинг 4.1  Класс Vehicle # # MicroPython for the IoT Second Edition # # Class Example: A generic vehicle # # Dr. Charles Bell # class Vehicle: """Base class for defining vehicles""" axles = 0 doors = 0 occupants = 0 def __init__(self, num_axles, num_doors): self.axles = num_axles self.doors = num_doors def get_axles(self): return self.axles def get_doors(self): return self.doors def add_occupant(self): self.occupants += 1 def num_occupants(self): return self.occupants Пояснение к строке в тройных кавычках см. далее после листинга 4.3. Обратите внимание на пару вещей. Во-первых, есть метод с именем __ init__(). Это конструктор, который вызывается при создании экземпляра класса. В этом методе вы помещаете весь свой код инициализации, например установки для переменных. У нас также есть методы для возврата количества осей (axles), дверей (doors) и пассажиров (occupants). В этом классе у нас есть также один метод, позволяющий добавить пассажиров (add_occupant). Еще обратите внимание, что мы обращаемся к каждому атрибуту класса (т. е. к данным) с помощью self.<name>. Таким образом мы можем гарантировать, что всегда имеем доступ к данным, связанным с созданным экземпляром, а не к глобальной переменной или другой локальной переменной. Давайте посмотрим, как этот класс можно использовать для определения семейного седана. В лис­тин­ге 4.2 показан код, используемый в этом классе. Мы можем поместить этот код в файл с именем sedan.py.
140  Как программировать на MicroPython Листинг 4.2  Использование класса Vehicle # # MicroPython for the IoT Second Edition # # Class Example: Using the generic Vehicle class # # Dr. Charles Bell # from vehicle import Vehicle sedan = Vehicle(2, 4) sedan.add_occupant() sedan.add_occupant() sedan.add_occupant() print("The car has {0} occupants.".format(sedan.num_occupants())) Обратите внимание, что первая строка импортирует класс Vehicle из модуля vehicle. Обратите также внимание, что я написал имя класса, а не имя файла. Это очень распространенная схема именования. Далее в коде мы создаем экземпляр класса. Обратите внимание, что я передал значения 2, 4 имени класса. Это приведет к вызову метода __init__() при создании экземпляра класса. Переменная sedan становится переменной экземпляра класса (объектом), которой мы можем манипулировать, и я делаю это, добавляя трех пассажиров, а затем распечатывая их количество, используя метод класса Vehicle. Мы можем запустить код на нашем ПК, используя команду ниже. Как мы видим, при запуске кода он сообщает нам, что в автомобиле находятся три пассажира. Отлично! $ python ./sedan.py The car has 3 occupants. Теперь давайте посмотрим, как мы можем использовать класс Vehicle для демонстрации наследования. В этом случае мы создадим новый класс с именем PickupTruck, который будет использовать класс Vehicle, но добавит специализацию к полученному классу. В лис­тин­ге 4.3 показан новый класс. Я поместил этот код в файл с именем Pickup_truck.py. Как вы увидите, пикап – это тип транспортного средства. Листинг 4.3  Класс Pickup Truck # # MicroPython for the IoT Second Edition # # Class Example: Inheriting the Vehicle class to form a # model of a pickup truck with maximum occupants and maximum # payload. # # Dr. Charles Bell # from vehicle import Vehicle
Модульность: модули, функции и классы  141 class PickupTruck(Vehicle): """This is a pickup truck that has: axles = 2, doors = 2, __max occupants = 3 The maximum payload is set on instantiation. """ occupants = 0 payload = 0 max_payload = 0 def __init__(self, max_weight): super().__init__(2,2) self.max_payload = max_weight self.__max_occupants = 3 def add_occupant(self): if (self.occupants < self.__max_occupants): super().add_occupant() else: print("Sorry, only 3 occupants are permitted in the truck.") def add_payload(self, num_pounds): if ((self.payload + num_pounds) < self.max_payload): self.payload += num_pounds else: print("Overloaded!") def remove_payload(self, num_pounds): if ((self.payload - num_pounds) >= 0): self.payload -= num_pounds else: print("Nothing in the truck.") def get_payload(self): return self.payload Обратите внимание на несколько вещей. Во-первых, обратите внимание на оператор класса: class PickupTruck(Vehicle). Когда мы хотим наследовать от другого класса, мы добавляем в круглые скобки имя базового класса. Это гарантирует, что Python будет использовать базовый класс, позволяя производному классу использовать все доступные данные и память. Если вы хотите наследовать более чем один класс, вы можете (так называемое мно­жественное наследование) просто перечислить базовые (родительские) классы списком, разделенным запятыми. Далее обратите внимание на переменную __max_occupants. Использование двух символов подчеркивания в классе для атрибута или метода по соглашению делает элемент частным для класса1. То есть доступ к нему следует осуществлять только внутри класса. Ни один вызывающий класс (через переменную/экземпляр класса) не может получить доступ к частным элементам, 1 Технически это называется искажением имен (name mangling), которое имитирует создание чего-то частного, но к нему все равно можно получить доступ, если вы укажете правильное количество символов подчеркивания. См. https://en.wikipedia. org/wiki/Name_mangling. – Прим. перев.
142  Как программировать на MicroPython как и любой класс, производный от этого класса. Всегда полезно скрывать атрибуты (данные). Вам может быть интересно, что случилось с методами пассажира (occupant). Почему они не в новом классе? Их там нет, потому что наш новый класс унаследовал все это поведение от базового класса. Мало того, код был изменен, чтобы ограничить число пассажиров ровно тремя. Я также хочу отметить документацию, которую добавил в класс. Мы используем строки документации (строки, в которых до и после используется набор из трех двойных кавычек) для документирования класса. Вы можете разместить здесь документацию, объясняющую класс и его методы. Чуть позже мы увидим хорошее использование этого. Наконец, обратите внимание на код в конструкторе. Здесь показано, как вызвать метод базового класса, что я и делаю, чтобы установить количество осей и дверей. Мы могли бы сделать то же самое в других методах, если бы захотели вызвать версию метода базового класса. Теперь давайте напишем код для использования этого класса. В листинге 4.4 показан код для тестирования данного класса. Здесь мы создаем файл с именем Pickup.py, который создает экземпляр PickupTruck, добавляет пассажиров и полезную нагрузку, а затем распечатывает содержимое. Листинг 4.4  Использование класса PickupTruck # # MicroPython for the IoT Second Edition # # Class Example: Exercising the PickupTruck class. # # Dr. Charles Bell # from pickup_truck import PickupTruck pickup = PickupTruck(500) pickup.add_occupant() pickup.add_occupant() pickup.add_occupant() pickup.add_occupant() pickup.add_payload(100) pickup.add_payload(300) print("Number of occupants in truck = {0}.".format(pickup.num_occupants())) print("Weight in truck = {0}.".format(pickup.get_payload())) pickup.add_payload(200) pickup.remove_payload(400) pickup.remove_payload(10) Обратите внимание: я добавляю пару вызовов метода add_occupant(), который новый класс наследует и переопределяет. Я также добавляю вызовы, чтобы мы могли протестировать код в методах, которые проверяют наличие чрезмерного количества занимающих и максимальную полезную нагрузку. Когда мы запустим этот код, мы увидим следующие результаты:
Модульность: модули, функции и классы  143 $ python ./pickup.py Sorry, only 3 occupants are permitted in the truck. Number of occupants in truck = 3. Weight in truck = 400. Overloaded! Nothing in the truck. Я запустил этот код на своем ПК, но я могу запустить весь этот код и на плате MicroPython и увидеть те же результаты. Есть еще одна вещь, которую нам следует изучить о классах: встроенные атрибуты. Вспомните метод __init__(). Python автоматически предоставляет несколько встроенных атрибутов, каждый из которых начинается с __, которые вы можете использовать, чтобы больше узнать об объектах. Ниже перечислены некоторые операторы, доступные для классов:      __dict__: словарь, содержащий пространство имен класса; __doc__: строка документации класса; __name__: имя класса; __module__: имя модуля, в котором определен класс; __bases__: базовый(е) класс(ы) в порядке наследования. Ниже показано, что именно каждый из этих атрибутов возвращает для класса PickupTruck. Я добавил этот текст в файл Pickup.py: print("PickupTruck.__doc__:", PickupTruck.__doc__) print("PickupTruck.__name__:", PickupTruck.__name__) print("PickupTruck.__module__:", PickupTruck.__module__) print("PickupTruck.__bases__:", PickupTruck.__bases__) print("PickupTruck.__dict__:", PickupTruck.__dict__) When this code is run, we see the following output. PickupTruck.__doc__: This is a pickup truck that has: axles = 2, doors = 2, max occupants = 3 The maximum payload is set on instantiation. PickupTruck.__name__: PickupTruck PickupTruck.__module__: pickup_truck PickupTruck.__bases__: (<class 'vehicle.Vehicle'>,) PickupTruck.__dict__: {'__module__': 'pickup_truck', '__doc__': 'This is a pickup truck that has:\n axles = 2,\n doors = 2,\n max occupants = 3\n The maximum payload is set on instantiation.\n ', 'occupants': 0, 'payload': 0, 'max_payload': 0, ' _PickupTruck__max_occupants': 3, '__init__': <function PickupTruck.__ init__ at 0x1018a1488>, 'add_occupant': <function PickupTruck.add_occupant at 0x1018a17b8>, 'add_payload': <function PickupTruck.add_payload at 0x1018a1840>, 'remove_payload': <function PickupTruck.remove_payload at 0x1018a18c8>, 'get_payload': <function PickupTruck. get_payload at 0x1018a1950>} Вы можете использовать встроенные атрибуты всякий раз, когда вам нужна дополнительная информация о классе. Обратите внимание на запись _PickupTruck__max_occupants в словаре. Напомним, что мы создали псевдочастную переменную __max_occupants. Здесь мы видим, как Python обращается
144  Как программировать на MicroPython к переменной, добавляя к переменной имя класса. Помните, что переменные, начинающиеся с двух символов подчеркивания (а не одного), указывают на то, что их следует считать частными для класса и использовать только внутри класса. СОВЕТ Дополнительную информацию о классах Python см. https://docs.python.org/3/ tutorial/classes.html. Теперь давайте посмотрим несколько примеров программ на Python, которые мы можем использовать на практике. Как и предыдущие примеры, вы можете писать и выполнять их либо на своем ПК, либо на плате MicroPython. Изучение Python на примерах Лучший способ научиться программировать на любом языке – практиковаться на примерах. В этом разделе я представлю несколько примеров, которые вы можете использовать для практики программирования на Python. Для запуска этих примеров вы можете использовать плату MicroPython или компьютер. Я представляю первые два примера с использованием моего ПК через консоль Python, а вторые два – с использованием платы MicroPython через консоль REPL. Код каждого примера подробно разъясняется, демонстрируются выходные результаты работы примера при выполнении кода. Также предлагаю вам попробовать одну или две модификации каждого примера самостоятельно. Я советую вам реализовать эти примеры и самостоятельно решить задачи в качестве практики для проектов, описанных далее в этой книге. Пример 1. Использование циклов В этом примере показано, как организовать циклы на Python на примере цикла for. Проблема, которую мы пытаемся решить, – это преобразование целых чисел из десятичных в двоичные, шестнадцатеричные и восьмеричные. Часто в проектах интернета вещей нам необходимо видеть значения в одном или нескольких из этих форматов, и в некоторых случаях используемые нами датчики (и соответствующая документация) применяют шестнадцатеричный, а не десятичный формат. Таким образом, этот пример может быть полезен в будущем не только для использования цикла for, но и для преобразования целых чисел в различные форматы. Написание кода Пример начинается с кортежа целых чисел, которые необходимо преобразовать. Кортежи и списки можно перебирать (значения читаются по порядку)
Изучение Python на примерах  145 с помощью цикла for. Напомним, что кортеж доступен только для чтения, в данном случае, поскольку он является входным, это нормально, но в других случаях, когда вам может потребоваться изменить значения, вам захочется использовать обычный список. Напомним, синтаксическая разница между кортежем и списком заключается в том, что в кортеже используются круглые скобки, а в списке – квадратные. Показанный здесь цикл for называется циклом «для каждого». Обратите внимание, что я использовал синтаксис извлечения значений (value in values), сообщающий Python о необходимости перебирать кортеж с именами значений, извлекая (сохраняя) значение каждого элемента в переменную на каждой итерации кортежа. Наконец, я использую функции print() и format() для замены двух образцов {0} и {1}, чтобы отобразить нужный формат целого числа с применением методов bin() для двоичного, oct() для восьмеричного и hex() для шестнадцатеричного чисел, которые выполняют преобразование за нас. В лис­тин­ ге 4.5 показаны примеры преобразования целых чисел в различные формы. Листинг 4.5  Преобразование целых чисел # # MicroPython for the IoT Second Edition # # Example: Convert integer to binary, hex, and octal # # Dr. Charles Bell # # Create a tuple of integer values values = (12, 450, 1, 89, 2017, 90125) # Loop through the values and convert each to binary, hex, and octal for value in values: print("{0} in binary is {1}".format(value, bin(value))) print("{0} in octal is {1}".format(value, oct(value))) print("{0} in hexadecimal is {1}".format(value, hex(value))) Выполнение кода Вы можете сохранить этот код в файле с именем conversions.py на своем компьютере, а затем открыть терминал (окно консоли) и запустить код с помощью команды python ./conversions.py (или python3, если у вас установлено несколько версий Python). В лис­тин­ге 4.6 показаны выходные результаты. Листинг 4.6  Пример вывода результатов преобразований $ python3 ./conversions.py 12 in binary is 0b1100 12 in octal is 0o14 12 in hexadecimal is 0xc 450 in binary is 0b111000010
146  Как программировать на MicroPython 450 in octal is 0o702 450 in hexadecimal is 0x1c2 1 in binary is 0b1 1 in octal is 0o1 1 in hexadecimal is 0x1 89 in binary is 0b1011001 89 in octal is 0o131 89 in hexadecimal is 0x59 2017 in binary is 0b11111100001 2017 in octal is 0o3741 2017 in hexadecimal is 0x7e1 90125 in binary is 0b10110000000001101 90125 in octal is 0o260015 90125 in hexadecimal is 0x1600d Обратите внимание, что все значения в кортеже были преобразованы. Внесение изменений Чтобы улучшить этот пример, вместо использования статического кортежа для хранения жестко запрограммированных целых чисел перепишите пример так, чтобы считывать целое число из аргументов в командной строке вместе с указанием желаемого формата. Например, код будет выполняться следующим образом: $ python ./conversions.py 123 hex 123 in hexadecimal is 0x7b Чтобы прочитать аргументы из командной строки, используйте анализатор аргументов argparse (https://docs.python.org/3/howto/argparse.html). Если вы хотите прочитать целое число из командной строки, вы можете использовать модуль argparse для добавления аргумента по имени следующим образом: import argparse # Setup the argument parser parser = argparse.ArgumentParser() # We need two arguments: integer, and conversion parser.add_argument("original_val") parser.add_argument("conversion") # Get the arguments args = parser.parse_args() Когда вы используете модуль анализатора аргументов (argparse), все значения аргументов представляют собой строки, поэтому вам нужно будет преобразовать значение в целое число, прежде чем использовать метод bin(), hex() или oct(). Вам также необходимо будет определить, какое преобразование запрашивается. Я предлагаю использовать для преобразования только шестнадцатеричные (hex), двоичные (bin) и восьмеричные (oct) значения и использовать
Изучение Python на примерах  147 набор условий для проверки запрошенного преобразования. Что-то вроде следующего будет работать: if args.conversion == 'bin': # do conversion to binary elif args.conversion == 'oct': # do conversion to octal elif args.conversion == 'hex': # do conversion to hexadecimal else: print("Sorry, I don't understand, {0}.".format(args.conversion)) Обратите внимание, что последнее else сообщает, что аргумент не был распознан. Это помогает управлять ошибками пользователя. Есть еще одна вещь, связанная с анализатором аргументов, которую вам следует знать. Вы можете передать строку справки при добавлении аргументов. Анализатор аргументов бесплатно предоставляет вам аргумент справки (-h). Обратите внимание на следующее: я добавил пару строк, используя параметр, названный help=: # We need two arguments: integer, and conversion parser.add_argument("original_val", help="Value to convert.") parser.add_argument("conversion", help="Conversion options: hex, bin, or oct.") Теперь, когда мы завершим код и запустим его с опцией -h, мы получим следующий результат. Круто, да? $ python ./conversions.py -h usage: conversions.py [-h] original_val conversion positional arguments: original_val Value to convert. conversion Conversion options: hex, bin, or oct. optional arguments: -h, --help show this help message and exit Пример 2. Использование сложных данных и файлов В этом примере показано, как работать с JavaScript Object Notation (JSON)1 в Python. Если коротко, то JSON – это язык разметки, используемый для обмена данными. Он не только удобен для чтения человеком, но также может использоваться непосредственно в приложениях для хранения и извлечения данных из других приложений, серверов и даже баз данных MySQL. JSON кажется программистам знакомым, поскольку он похож на другие схемы разметки данных или текста. JSON – также довольно простой язык, так как 1 www.json.org. – Прим. авт.
148  Как программировать на MicroPython поддерживает только два типа структур: 1) коллекцию, содержащую пары (имя, значение), и 2) упорядоченный список (или массив). Конечно, вы также можете смешивать и сочетать структуры объекта. Когда мы создаем объект JSON, мы называем его документом JSON. Проблема, которую мы попытаемся решить, – это запись данных в файлы и чтение из файлов. В этом случае мы будем использовать специальный Python-модуль кодирования и декодирования JSON с именем json, который позволяет нам легко конвертировать данные в файлах (или других потоках) в JSON и из него. Как вы увидите, получить доступ к данным JSON несложно, просто используя имена ключей (иногда называемых полями) для доступа к данным. Таким образом, этот пример может быть полезен в будущем не только для использования файлов для чтения и записи, но и вообще для работы с документами JSON. Написание кода В этом примере данные сохраняются и извлекаются в файлах. Данные представляют собой основную информацию о домашних животных, включая их имя, возраст, породу и тип. Тип используется для определения общих категорий, таких как рыба, собака или кошка. Начнем с импорта модуля JSON (с именем json), встроенного в платформу MicroPython. Затем мы подготавливаем некоторые исходные данные, сохраняя их в списке Python. Мы используем метод json.loads() для передачи строки в формате JSON. Результатом является документ JSON, который мы можем добавить в наш список. В примерах применяется очень простая форма документов JSON – коллекция пар (имя, значение). Ниже показан пример одной из используемых строк в формате JSON: {"name":"Violet", "age": 11, "breed":"dachshund", "type":"dog"} Обратите внимание, что мы заключаем строку в фигурные скобки и используем ряд имен ключей, двоеточие и значение, разделенные запятыми. Если это выглядит знакомо, то это потому, что это тот же формат, что и словарь Python. Это демонстрирует мой комментарий о том, что синтаксис JSON кажется программистам знакомым. Метод JSON json.loads() принимает строку в формате JSON, потом анализирует строку, проверяя ее достоверность, и возвращает документ JSON. Затем мы сохраняем этот документ в переменной и добавляем его в список следующим образом: parsed_json = json.loads('{"name":"Violet", "age": 11, "breed":"dachshund", "type":"dog"}') pets.append(parsed_json) После добавления данных в список мы записываем их в файл с именем my_data.json. Для работы с файлами мы сначала открываем файл с помощью функции open(), которая принимает имя файла (включая путь, если вы хотите поместить файл в каталог) и режим доступа: «r» для чтения и «w» для записи.
Изучение Python на примерах  149 Вы также можете использовать «а» для добавления, если хотите открыть файл и добавлять в его конец. Обратите внимание, что доступ «w» перезапишет файл при записи в него. Если функция open() завершается успешно, вы получаете файловый объект, который позволяет вызывать дополнительные функции для чтения или записи данных. Функция open() завершится ошибкой, вы запросили доступ на чтение, но файл отсутствует или у вас нет разрешений на запись в файл. В табл. 4.3 показан полный список режимов, доступных для функции open(). Таблица 4.3. Режимы доступа к файлам для функции open()в Python Режим Описание r Открывает файл только для чтения. Указатель файла помещается в начало файла. Это режим по умолчанию rb Открывает файл только для чтения в двоичном формате. Указатель файла помещается в начало файла. Это режим по умолчанию r+ Открывает файл как для чтения, так и для записи. Указатель файла помещается в начало файла rb+ Открывает файл для чтения и записи в двоичном формате. Указатель файла помещается в начало файла w Открывает файл только для записи. Перезаписывает файл, если файл существует. Если файл не существует, создается новый файл для записи wb Открывает файл для записи только в двоичном формате. Перезаписывает файл, если файл существует. Если файл не существует, создается новый файл для записи w+ Открывает файл как для записи, так и для чтения. Перезаписывает существующий файл, если файл существует. Если файл не существует, создается новый файл для чтения и записи wb+ Открывает файл для записи и чтения в двоичном формате. Перезаписывает существующий файл, если файл существует. Если файл не существует, создается новый файл для чтения и записи a Открывает файл для добавления. Указатель файла находится в конце файла, если файл существует. Если файл не существует, создается новый файл для записи ab Открывает файл для добавления в двоичном формате. Указатель файла находится в конце файла, если файл существует. Если файл не существует, создается новый файл для записи a+ Открывает файл как для добавления, так и для чтения. Указатель файла находится в конце файла, если файл существует. Если файл не существует, создается новый файл для чтения и записи ab+ Открывает файл для добавления и чтения в двоичном формате. Указатель файла находится в конце файла, если файл существует. Если файл не существует, создается новый файл для чтения и записи Как только файл открыт, мы можем записать в него документы JSON, перебирая список. Итерация означает начало с первого элемента и поочередный доступ к элементам списка (в том порядке, в котором они появляются в спис­ ке). Напомним, итерация в Python очень проста. Мы просто говорим «для каждого элемента в списке» с помощью цикла for следующим образом:
150  Как программировать на MicroPython for pet in pets: // do something with the pet data Чтобы записать документ JSON в файл, мы используем метод json.dumps(), который создает строку в формате JSON и записывает ее в файл с помощью переменной файла и метода write(). Таким образом, теперь мы видим, как создавать документы JSON из строк, а затем декодировать (преобразовывать) их обратно в строку. После того как мы записали данные в файл, мы закрываем файл с помощью функции close(), затем снова открываем его и читаем данные из файла. В нашем случае мы используем другую специальную реализацию цикла for. Мы используем переменную файла для чтения всех строк в файле с помощью метода readlines(), а затем перебираем их с помощью следующего кода: json_file = open("my_data.json", "r") for pet in json_file.readlines(): // do something with the pet string Мы снова используем метод json.loads(), чтобы прочитать строку в формате JSON из файла и затем преобразовать ее в документ JSON, который мы добавляем в другой список. Затем закрываем файл. Теперь данные считаны обратно в нашу программу, и мы можем их использовать. Наконец, мы перебираем новый список и выводим на экран данные из документов JSON, используя имена ключей. В лис­тин­ге 4.7 показан готовый код этого примера. Листинг 4.7  Запись объектов JSON в файлы и чтение из файлов # # MicroPython for the IoT Second Edition # # Example: Storing and retrieving JSON objects in files # # Dr. Charles Bell # import json # Prepare a list of JSON documents for pets by converting JSON to a dictionary pets = [] parsed_json = json.loads('{"name":"Violet", "age": 11, "breed":"dachshund", "type":"dog"}') pets.append(parsed_json) parsed_json = json.loads('{"name": "JonJon", "age": 20, "breed":"poodle", "type":"dog"}') pets.append(parsed_json) parsed_json = json.loads('{"name": "Mister", "age": 9, "breed":"siberian khatru", "type":"cat"}') pets.append(parsed_json) parsed_json = json.loads('{"name": "Spot", "age": 12, "breed":"koi", "type":"fish"}') pets.append(parsed_json) parsed_json = json.loads('{"name": "Charlie", "age": 11, "breed":"dachshund", "type":"dog"}')
Изучение Python на примерах  151 pets.append(parsed_json) # Now, write these entries to a file. # Note: overwrites the file json_file = open("my_data.json", "w") for pet in pets: json_file.write(json.dumps(pet)) json_file.write("\n") json_file.close() # Now, let's read the JSON documents then print the name and # age for all of the dogs in the list my_pets = [] json_file = open("my_data.json", "r") for pet in json_file.readlines(): parsed_json = json.loads(pet) my_pets.append(parsed_json) json_file.close() print("Name, Age") for pet in my_pets: if pet['type'] == 'dog': print("{0}, {1}".format(pet['name'], pet['age'])) Обратите внимание на цикл записи данных. Мы добавили второй метод write(), передающий странную строку «\n» – это особый символ, означающий новую строку. Это заставляет строки в формате JSON располагаться в отдельных строках файла и повышает читаемость. СОВЕТ Чтобы узнать больше о работе с файлами в Python, см. документацию по адресу https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files. Итак, как выглядит файл? Ниже приведен дамп файла с использованием утилиты more, который показывает содержимое файла. Обратите внимание, что файл содержит строки в формате JSON, как и в нашем коде: $ more my_data.json {"age": 11, "breed": "dachshund", "type": "dog", "name": "Violet"} {"age": 20, "breed": "poodle", "type": "dog", "name": "JonJon"} {"age": 9, "breed": "siberian khatru", "type": "cat", "name": "Mister"} {"age": 12, "breed": "koi", "type": "fish", "name": "Spot"} {"age": 11, "breed": "dachshund", "type": "dog", "name": "Charlie"} Теперь давайте посмотрим, что произойдет, когда мы запустим этот скрипт. Выполнение кода Вы можете сохранить этот код в файле с именем rw_json.py на своем компьютере, затем открыть терминал (окно консоли) и запустить код с помощью команды python ./rw_json.py (или python3, если у вас установлено несколько версий Python). Ниже показан результат: $ python ./rw_json.py Name, Age Violet, 11
152  Как программировать на MicroPython JonJon, 20 Charlie, 11 Хотя результат может быть не очень впечатляющим, выполнив этот пример, вы многое узнали о работе с файлами и структурированными данными с использованием документов JSON. Внесение изменений Чтобы сделать этот пример более сложным, вы можете изменить его, включив в него больше информации о ваших домашних животных. Я предлагаю начать с обычного текстового файла и ввести строки в формате JSON для ваших домашних животных. Чтобы усложнить задачу, попробуйте добавить информацию, относящуюся к типу домашнего животного. Например, вы можете добавить несколько ключей для одного или нескольких домашних животных, другие ключи для других домашних животных и т. д. Это продемонстрирует одну из возможностей документов JSON: коллекции в документах JSON не обязательно должны иметь одинаковый формат. Получив этот файл (my_pets), измените код для чтения из файла и распечатки всей информации о каждом домашнем животном, распечатав имя и значение ключа. Подсказка: вам нужно будет использовать специальный код для распечатки имени ключа и значения, называемый «красивая печать». Например, приведенный ниже код распечатает документ JSON в легко читаемом формате. Обратите внимание, что мы используем опцию sort_keys для печати ключей (полей) и можем контролировать количество пробелов для отступа: for pet in my_pets: print(json.dumps(pet, sort_keys=True, indent=4)) После запуска результат будет выглядеть следующим образом: { "age": 11, "breed": "dachshund", "name": "Violet", "type": "dog" } { "age": 20, "breed": "poodle", "name": "JonJon", "type": "dog" } Пример 3. Использование функций В этом примере будет показано, как создавать и использовать функции. Вызываемые функции используются, чтобы сделать наш код более модульным.
Изучение Python на примерах  153 Функции также могут быть инструментом во избежание дублирования кода. То есть мы можем повторно использовать части кода, помещая их в функцию. Функции еще используются, чтобы помочь изолировать код для специальных операций, таких как математические формулы. Проблема, которую мы исследуем в этом примере, заключается в том, как создавать функции для выполнения вычислений. Мы рассмотрим распространенный метод информатики, называемый рекурсией1, при котором функция многократно вызывает сама себя. Я также покажу вам ту же функцию, реа­ лизованную обычным итеративным способом (обычно с использованием цикла). Хотя некоторые советуют избегать рекурсии, рекурсивные функции немного короче в написании, но сложнее в отладке, если что-то пойдет не так. Лучший совет, который я могу дать, заключается в том, что почти каждая рекурсивная функция может быть написана как итеративная функция, и начинающим программистам следует придерживаться итеративных решений, пока они не приобретут уверенность в использовании функций. Написание кода Этот пример предназначен для расчета ряда Фибоначчи2. Ряд Фибоначчи рассчитывается как сумма двух предыдущих значений ряда. Ряд начинается с 1, за которым следует 1 (ничего плюс 1), затем 1 + 1 = 2 и т. д. В этом примере мы запрашиваем у пользователя целое число, а затем вычисляем запрошенное количество значений в ряду Фибоначчи. Если на входе 5, серия равна 1, 1, 2, 3, 5. Мы создадим две функции: одну для вычисления ряда Фибоначчи с использованием кода, который итеративно вычисляет ряд, и одну для вычисления n-го числа Фибоначчи с использованием рекурсивной функции. Давайте сначала посмотрим на итеративную функцию. Чтобы определить функцию, мы используем синтаксис def func_name(<pa­ra­ meters>), где указываем имя функции и список из нуля или более параметров, за которыми следует двоеточие. Эти параметры затем можно использовать внутри функции, в том числе и для передачи данных в функцию. Ниже показана итеративная версия кода ряда Фибоначчи. Мы назовем эту функцию fibonacci_iterative: def fibonacci_iterative(count): i = 1 if count == 0: fib = [] elif count == 1: fib = [1] 1 2 https://en.wikipedia.org/wiki/Recursion_(computer_science). – Прим. авт. Русскоязычный аналог: https://ru.wikipedia.org/wiki/Рекурсия#В_программировании. – Прим. перев. https://en.wikipedia.org/wiki/Fibonacci_number. – Прим. авт. Русскоязычный аналог: https://ru.wikipedia.org/wiki/Числа_Фибоначчи. – Прим. перев.
154  Как программировать на MicroPython elif count == 2: fib = [1,1] elif count > 2: fib = [1,1] while i < (count - 1): fib.append(fib[i] + fib[i-1]) i += 1 return fib Этот код просто вычисляет первые N значений в серии и возвращает их в виде списка. Параметр count – это количество значений в серии. Функция начинается с проверки того, запрошены ли тривиальные значения: 0, 1 или 2, значения которых известны. Если значение count больше 2, мы начинаем с известной серии [1, 1], затем используем цикл для вычисления следующего значения путем сложения двух предыдущих значений. Обратите внимание, как я использую индекс списка, чтобы получить два предыдущих значения в списке (i и i-1). Мы будем использовать эту функцию и список, возвращаемый непосредственно в коде, чтобы найти определенное значение в серии и вывести его на экран. Теперь давайте посмотрим на рекурсивную версию функции. Мы назовем эту функцию fibonacci_recursive: def fibonacci_recursive(number): if number == 0: return 0 elif number == 1: return 1 else: # Call our self counting down value = fibonacci_recursive(number-1) + fibonacci_recursive(number-2) return value В этом случае мы не возвращаем всю серию; скорее, мы возвращаем конкретное n-е значение в серии. Как и в итеративном примере, мы делаем то же самое с тривиальными значениями, возвращающими запрошенное число. В противном случае мы снова вызываем одну и ту же функцию для каждого числа. Может потребоваться некоторое время, чтобы понять, как это работает, но n-е значение будет вычисляться. Теперь вам может быть интересно, где в коде размещаются функции. Их необходимо разместить в верхней части (начале) кода. Python проанализирует функции и продолжит выполнять инструкции, соответствующие определениям. Таким образом, мы размещаем наш «основной» код после наших функций. Основной код этого примера начинается с запроса n-го значения ряда Фибоначчи, а потом сначала используется рекурсивная функция для вычисления значения. Затем мы спрашиваем пользователя, хочет ли он просмотреть всю серию, и если да, то используем итеративную версию функции, чтобы получить список и распечатать его. Мы распечатываем n-е значение и снова
Изучение Python на примерах  155 предоставляем возможность просмотреть всю серию, чтобы показать, что результат одинаковый при использовании обеих функций. В лис­тин­ге 4.8 показан готовый код этого примера. Мы назовем этот код fibonacci.py. Листинг 4.8  Вычисление ряда Фибоначчи # # MicroPython for the IoT Second Edition # # Example: Fibonacci series using recursion # # Calculate the Fibonacci series based on user input # # Dr. Charles Bell # # Create a function to calculate Fibonacci series (iterative) # Returns a list. def fibonacci_iterative(count): i = 1 if count == 0: fib = [] elif count == 1: fib = [1] elif count == 2: fib = [1,1] elif count > 2: fib = [1,1] while i < (count - 1): fib.append(fib[i] + fib[i-1]) i += 1 return fib # Create a function to calculate the nth Fibonacci number (recursive) # Returns an integer. def fibonacci_recursive(number): if number == 0: return 0 elif number == 1: return 1 else: # Call our self counting down. value = fibonacci_recursive(number-1) + fibonacci_recursive(number-2) return value # Main code print("Welcome to my Fibonacci calculator!") index = int(input("Please enter the number of integers in the series: ")) # Recursive example print("We calculate the value using a recursive algoritm.") nth_fibonacci = fibonacci_recursive(index) print("The {0}{1} fibonacci number is {2}." "".format(index, "th" if index > 1 else "st", nth_fibonacci)) see_series = str(input("Do you want to see all of the values in the series? ")) if see_series in ["Y","y"]:
156  Как программировать на MicroPython series = [] for i in range(1,index+1): series.append(fibonacci_recursive(i)) print("Series: {0}: ".format(series)) # Iterative example print("We calculate the value using an iterative algoritm.") series = fibonacci_iterative(index) print("The {0}{1} fibonacci number is {2}." "".format(index, "th" if index > 1 else "st", series[index-1])) see_series = str(input("Do you want to see all of the values in the series? ")) if see_series in ["Y","y"]: print("Series: {0}: ".format(series)) print("bye!") Уделите несколько минут изучению кода. Хотя решаемая проблема немного проще, чем в предыдущем примере, здесь требуется гораздо больше кода. Когда вы будете готовы, подключите плату MicroPython и создайте файл. Для этого примера вы создаете файл на своем компьютере и называете его fibonacci.py. Мы скопируем его на плату MicroPython в следующем разделе. СОВЕТ Дополнительную информацию по определению функций см. в документации по адресу https://docs.python.org/3/tutorial/controlflow.html#defining-functions. Теперь давайте посмотрим, что произойдет, когда мы запустим этот скрипт. Напомним, мы будем запускать данный код на плате MicroPython, поэтому, прежде чем следовать инструкциям, обязательно настройте свою плату и подключите ее к компьютеру. Выполнение кода Вспомним главу 3: когда мы хотим переместить код на плату MicroPython, нужно создать файл, скопировать его на плату MicroPython и затем выполнить. Мы можем использовать Thonny для создания нового файла модуля Фибоначчи. Если вы хотите продолжить, сначала подключите плату MicroPython к компьютеру, затем откройте Thonny и выберите меню File (Файл) ⇒ New (Новый). Вы должны увидеть новый файл, как показано на рис. 4.1. Затем вы можете сохранить файл как fibonacci.py, используя меню File (Файл) ⇒ Save (Сохранить), и указать, что хотите сохранить его на MicroPython Board (Thonny спросит вас, где сохранить файл). Если вы следуете инструкциям, введите код, а затем сохраните файл на своей плате MicroPython. Вы должны увидеть, что файл появится в меню файлов платы, как показано на рис. 4.2. Далее мы продемонстрируем, как использовать модуль с помощью консоли REPL, расположенной в нижней части Thonny. Вы должны увидеть приветственное сообщение от MicroPython, за которым следует приглашение REPL (>>>). В командной строке REPL введите следующий оператор: import fibonacci
Изучение Python на примерах  157 Рис. 4.1  Новый файл (Thonny) Рис. 4.2  Файл Фибоначчи, сохраненный на плате MicroPython (Thonny) Эта команда импортирует файл fibonacci.py, который мы только что скопировали, и выполнит код. Итак, мы как будто запустили его на нашем ПК из
158  Как программировать на MicroPython консоли Python. Давайте протестируем программу, запросив двенадцатое значение ряда Фибоначчи. Вы должны увидеть результат, показанный на рис. 4.3. Рис. 4.3  Вывод результата примера Фибоначчи (Thonny) Если вы хотите просмотреть серию, ответьте «Y» на запрос, и код напечатает серию. Обратите также внимание, что в коде используются обе версии написанной нами функции: итеративная и рекурсивная. Наконец, мы видим, что значения или выходные данные обеих функций – это одни и те же данные. На рис. 4.4 показан пример вывода, который вы увидите. ПРИМЕЧАНИЕ Если у вас еще нет платы MicroPython, вы можете запустить код со своего ПК с помощью команды python ./fibonacci.py. Если вы хотите запустить код еще раз, просто нажмите <CTRL>+<C>, чтобы остановить код, <CTRL>+<D>, дабы выполнить программный сброс, а затем снова выполните оператор импорта. Это перезапустит весь код. Или, если вы хотите запустить одну из функций, вы можете вызвать ее еще раз, импортировав с помощью следующего кода. Ниже показано, как это будет выглядеть при выполнении на плате MicroPython: >>> >>> [1, >>> >>> 13 from fibonacci import fibonacci_iterative fibonacci_iterative(6) 1, 2, 3, 5, 8] from fibonacci import fibonacci_recursive fibonacci_recursive(7)
Изучение Python на примерах  159 Рис. 4.4  Результат вывода серии (Thonny) После импорта таким способом вы можете запустить функции отдельно. Попробуйте их с разными значениями, чтобы посмотреть, как они себя ведут. Напомним, что функции были реализованы по-разному – итеративная версия возвращает список, а рекурсивная версия возвращает только последнее n-е значение. Внесение изменений Чтобы сделать этот пример более полезным, измените код для поиска в ряду Фибоначчи определенного целого числа. Попросите пользователя ввести целое число, а затем определите, является ли это значение допустимым значением Фибоначчи. Например, если пользователь вводит 144, код должен сообщить пользователю, что значение допустимо и является двенадцатым значением в серии. Эта задача потребует от вас переписать большую часть кода «основной» функциональности, и вы должны понять, как использовать функции по-новому. Пример 4. Использование классов Этот пример значительно усложняет задачу за счет введения понятия из объектно ориентированного программирования: классов. Напомним, что классы – это еще один способ обеспечения модульности нашего кода. Классы используются для моделирования данных и поведения этих данных. Кроме того, классы обычно размещаются в отдельном модуле кода (файле), который
160  Как программировать на MicroPython дополнительно разделяет код на модули. Если вам нужно изменить класс, вам может потребоваться только изменить код в модуле класса. Проблема, которую мы исследуем в этом примере, заключается в том, как разрабатывать решения с использованием классов и модулей кода. Мы создадим два файла: один для класса, а другой для основного кода. Написание кода Этот пример предназначен для преобразования римских цифр в целые десятичные числа. Например, если мы вводим значение вроде VIII (то есть восемь), то ожидаем увидеть целое число 8. Чтобы сделать задачу более интересной, мы также возьмем полученное целое число и преобразуем его обратно в римские цифры1. Римские цифры получаются в виде строки, где используются символы I – 1, V – 5, X – 10, L – 50, C – 100, D – 500 и M – 1000. Комбинации для других чисел получаются путем сложения числового значения нужных символов (например, 3 = III) или вычитания символа, размещенного перед другим символом (например, 4 = IV2). Ниже приведены некоторые примеры того, как это работает: 3 = III 15 = XV 12 = XII 24 = XXIV 96 = LXLVI 107 = CVII Это может показаться большой дополнительной работой, но учтите следующее: если мы сможем конвертировать из одного формата в другой, то сможем конвертировать и обратно без ошибок, используя код одного преобразования для проверки другого. Если при обратном преобразовании мы получим другое значение, мы знаем, что у нас есть проблема, которую необходимо исправить. Чтобы решить проблему, мы вынесем код преобразования римских цифр в отдельный файл (модуль кода) и создадим класс Roman_Numerals для хранения методов. В этом случае данные представляют собой отображение целых чисел на римские цифры. Однако Python «не помнит» порядок определения записей; мы должны использовать подкласс словаря, чтобы сохранить порядок. Причина, по которой нам необходимо сохранить порядок, заключается в том, что мы будем использовать ключи и их значения в математических вычислениях. Если записи не в порядке, математические вычисления будут неточными, и преобразование приведет к неверным значениям. Мы можем 1 2 Описание по-русски: https://ru.wikipedia.org/wiki/Римские_цифры. – Прим. перев. Отметим, что на циферблатах часов с римскими цифрами (и только там!) принято записывать число четыре сложением единиц (IIII), аналогично тройке. Этот обычай возник, вероятно, потому, что перевернутую IV прочесть труднее, чем IIII, и смотрится она не так эстетично. – Прим. перев.
Изучение Python на примерах  161 использовать подкласс OrderedDict, чтобы сохранить порядок записей. Заметьте, что мы просто используем OrderedDict как класс и передаем исходные элементы словаря. Обратите внимание на расположение круглых скобок и использование оператора импорта для применения класса OrderedDict: from collections import OrderedDict # Private dictionary of roman numerals __roman_dict = OrderedDict({ 'I': 1, 'IV': 4, 'V': 5, 'IX': 9, 'X': 10, 'XL': 40, 'L': 50, 'XC': 90, 'C': 100, 'CD': 400, 'D': 500, 'CM': 900, 'M': 1000, }) Обратите внимание на два подчеркивания перед названием словаря. Это специальное обозначение, которое помечает словарь как частную переменную класса. Это аспект Python для сокрытия информации, который рекомендуется использовать при проектировании объектов; всегда стремитесь скрыть данные, используемые внутри класса. Обратите также внимание, что кроме применения основных символов и их значений я использовал также несколько других значений. Я сделал это, чтобы упростить преобразование. В этом случае я добавил записи-исключения, которые представляют преобразования с вычитанием, такие как 4 (IV), 9 (IX) и т. д.1 Это делает преобразование немного проще и универсальнее. Мы также добавим два метода: convert_to_int(), который принимает строку римских цифр и преобразует ее в целое число, и convert_to_roman(), который принимает целое число и преобразует его в римское число. Вместо того чтобы объяснять каждую строку кода в методах, я предоставляю вам возможность прочитать код и увидеть, как он работает. Проще говоря, метод преобразования в целое число принимает каждый символ и получает его значение из словаря, суммируя значения. Здесь есть хитрость, требующая специальной обработки комбинаций с вычитанием (например, IX). Метод преобразования в римский формат немного проще, поскольку мы просто делим значение на наибольшее значение в словаре, 1 В списке автора присутствуют все шесть вариантов исключений, касающиеся записи с вычитанием вместо сложения. Например, число 99 нельзя записать как IC, оно записывается по правилам сложения, как XCIX. – Прим. перев.
162  Как программировать на MicroPython пока не достигнем нуля. В лис­тин­ге 4.9 показан код модуля класса, который сохраняется в файле с именем roman_numerals.py. Листинг 4.9  Roman Numeral Class # # MicroPython for the IoT Second Edition # # Example: Roman numerals class # # Convert integers to roman numerals # Convert roman numerals to integers # # Dr. Charles Bell # from collections import OrderedDict class Roman_Numerals: # Private dictionary of roman numerals __roman_dict = OrderedDict({ 'I': 1, 'IV': 4, 'V': 5, 'IX': 9, 'X': 10, 'XL': 40, 'L': 50, 'XC': 90, 'C': 100, 'CD': 400, 'D': 500, 'CM': 900, 'M': 1000, }) def convert_to_int(self, roman_num): value = 0 for i in range(len(roman_num)): if i > 0 and self.__roman_dict[roman_num[i]] > self.__roman_dict[roman_num[i - 1]]: value += self.__roman_dict[roman_num[i]] - 2 * self.__roman_dict[roman_num[i - 1]] else: value += self.__roman_dict[roman_num[i]] return value def convert_to_roman(self, int_value): # First, get the values of all of entries in the dictionary roman_values = list(self.__roman_dict.values()) roman_keys = list(self.__roman_dict.keys()) # Prepare the string roman_str = ""
Изучение Python на примерах  163 remainder = int_value # Loop through the values in reverse for i in range(len(roman_values)-1, -1, -1): count = int(remainder / roman_values[i]) if count > 0: for j in range(0,count): roman_str += roman_keys[i] remainder -= count * roman_values[i] return roman_str Если вы следуете этой главе, создайте на своем компьютере файл с этим кодом и назовите его roman_numerals.py. Мы скопируем его на плату MicroPython в следующем разделе. Теперь давайте посмотрим на основной код. Для этого нам просто нужно импортировать новый класс из модуля кода следующим образом. Это немного другая форма директивы импорта. В этом случае мы говорим Python включить класс Roman_Numerals из файла с именем roman_numerals: from roman_numerals import Roman_Numerals ПРИМЕЧАНИЕ Если бы модуль кода находился в подпапке (скажем, в roman), мы бы написали оператор импорта как from roman import Roman_Numerals, где мы указываем папки, используя точечную нотацию вместо косой черты. Остальная часть кода проста. Сначала мы запрашиваем у пользователя действительную строку римских цифр, затем преобразуем ее в целое число и используем это значение для обратного преобразования в римские цифры, выводя результат. Итак, вы видите, что вынесение класса в отдельный модуль упростило наш код, сделав его короче и проще в обслуживании. В лис­ тинге 4.10 показан полный основной код, сохраненный в файле с простым именем roman.py. Листинг 4.10  Преобразование римских цифр # # MicroPython for the IoT Second Edition # # Example: Convert roman numerals using a class # # Convert integers to roman numerals # Convert roman numerals to integers # # Dr. Charles Bell # from roman_numerals import Roman_Numerals roman_str = input("Enter a valid roman numeral: ") roman_num = Roman_Numerals() # Convert to roman numerals value = roman_num.convert_to_int(roman_str) print("Convert to integer: {0} = {1}".format(roman_str, value))
164  Как программировать на MicroPython # Convert to integer new_str = roman_num.convert_to_roman(value) print("Convert to Roman Numerals: {0} = {1}".format(value, new_str)) print("bye!") Если вы следуете этой главе, создайте на своем компьютере файл для этого кода и назовите его roman.py. Мы скопируем на нашу плату MicroPython в следующем разделе. Теперь давайте посмотрим, что произойдет, когда мы запустим этот скрипт. Напомним, мы будем запускать этот код на нашей плате MicroPython, поэтому если вы будете следовать инструкциям, обязательно настройте свою плату и подключите ее к компьютеру. Выполнение кода Мы хотим выполнить этот пример с платы MicroPython. Используйте Thonny, чтобы скопировать файлы, созданные на вашем компьютере, на плату Mic­roPython. Напомним, мы используем файловый менеджер в Thonny для перехода к файлам на нашем ПК, затем щелкаем правой кнопкой мыши по каждому файлу и выбираем «Загрузить в /», как показано на рис. 4.5. Рис. 4.5  Копирование файлов на плату MicroPython (Thonny) Обязательно скопируйте файлы roman.py и roman_numerals.py на свою плату MicroPython. Затем используйте консоль REPL в Thonny для запуска кода. В командной строке REPL введите следующий оператор:
Изучение Python на примерах  165 >>> import roman Этот код импортирует файл roman.py, который мы только что скопировали, и когда это произойдет, он выполняет код, который, если вы помните, вызовет код в модуле кода roman_numerals.py. Импорт аналогичен тому, как мы запускали это на нашем ПК из консоли Python. Как только выполнится оператор импорта, код сразу начнет выполняться. Начните со значения 60, которое римскими цифрами обозначается как LX: >>> import roman Enter a valid roman numeral: LX Convert to integer: LX = 60 Convert to Roman Numerals: 60 = LVV bye! Чтобы снова запустить код, вы можете выполнить мягкую перезагрузку, нажав клавиши <CTRL>+<D>, а затем повторно запустив оператор импорта. Если это кажется неудобным, вы правы, и есть лучший способ – просто импортируйте модуль Roman_Numerals, а затем вызовите функции вручную следующим образом: >>> from roman_numerals import Roman_Numerals >>> rn = Roman_Numerals() >>> rn.convert_to_int("MMXXIII") 2023 >>> rn.convert_to_roman(2023) 'MMXXIII' Продолжите тестировать код для максимально возможного количества различных комбинаций. Вы также можете попробовать использовать неправильно сформированные римские цифры, чтобы посмотреть, как код их обрабатывает. Подсказка: его необходимо будет настроить так, чтобы он был полностью терпим к неверным входным данным. Например, ниже показано использование неправильно сформированной римской цифры, преобразованной в целое число и обратно в римские цифры. В этом случае ошибочные римские цифры при преобразовании опускаются, что, возможно, не самое лучшее поведение при построении системы, толерантной к ошибкам, но для наших целей этого достаточно: >>> rn.convert_to_int("MIMVXIII") 2007 >>> rn.convert_to_roman(2007) 'MMVII' Внесение изменений К этому примеру особо нечего добавить, чтобы улучшить его, кроме, возможно, некоторого удобства для пользователя. Если вы хотите улучшить код или сам класс, я предлагаю добавить новый метод с именем validate() для
166  Как программировать на MicroPython проверки строки римских цифр. Этот метод определит, содержит ли входная строка допустимую последовательность символов. Подсказка: для начала убедитесь, что строка содержит только символы из словаря. Однако вы можете использовать данный шаблон при создании других классов для преобразования форматов. Например, в качестве упражнения вы можете создать новый класс для преобразования целых чисел в шестнадцатеричные или восьмеричные. Конечно, есть функции, которые сделают это за нас, но такое упражнение может быть полезно, и приятно выполнить его самостоятельно. Давайте попробуйте – создайте новый класс для преобразования целых чисел в другие форматы. Я бы предложил сначала выполнить функцию преобразования шестнадцатеричного числа в целое число, а когда она будет работать правильно, создать обратную величину для преобразования целых чисел в шестнадцатеричные. Более сложной задачей было бы переписать класс, чтобы он принимал строку в конструкторе (при создании переменной класса), и использовать эту строку для преобразований вместо передачи строки или целого числа с помощью методов convert_to. Например, класс может иметь конструктор и скрытый член следующим образом: __roman_str = "" ... def __init__(self, name): self.name = name Когда вы создаете экземпляр, вам нужно будет передать строку, иначе вы получите сообщение об отсутствии необходимого параметра: roman_str = input("Enter a valid roman numeral: ") roman_num = Roman_Numerals(roman_str) Дополнительная информация Если вам требуются более глубокие знания Python, по этой теме есть несколько отличных книг. Я приведу некоторые из моих фаворитов.   Прежде всего отличным ресурсом является документация на сайте Python: https://www.python.org/doc/.   Pro Python 3, 3rd Edition (Apress, 2019), J. Burton Browning, Marty Alchin.   Learning Python, 5th Edition (O’Reilly Media, 2013), Mark Lutz. Русский перевод: Лутц Марк. Изучаем Python. Т. 1. 5-е изд.; Лутц Марк. Изучаем Python. Т. 2. 5-е изд. М.: Вильямс, 2019.   Automate the Boring Stuff with Python: Practical Programming for Total Beginners, 2nd Edition (No Starch Press, 2019), Al Sweigart. Русский перевод: Свейгарт Эл. Автоматизация рутинных задач с помощью Python. М.: Диалектика, 2021.
Итоги  167 Итоги Это было тяжелое путешествие, не так ли? Я надеюсь, что этот краткий ускоренный курс Python достаточно объяснил показанные примеры программ, и теперь вы знаете, как они работают. Этот курс также формирует основу для понимания других примеров Python в этой книге. Если вы учитесь работать с проектами интернета вещей и не знаете, как программировать на Python, изучение Python может быть интересным, учитывая его простой для понимания синтаксис. Несмотря на то что в интернете есть множество примеров, которые вы можете использовать, очень немногие из них документированы таким образом, чтобы предоставить достаточно информации, для того чтобы новичок в Python мог понять или тем более начать работу и развернуть образец! Но по крайней мере код легко читается. В этой главе был представлен ускоренный курс Python, в котором рассмат­ ривались основы того, с чем вы столкнетесь при изучении большинства небольших примеров проектов. Мы узнали основной синтаксис и конструкции приложения Python с пошаговой инструкцией по созданию настоящего приложения Python, которое мигает светодиодом. На этом примере мы узнали, как работать с автономными приложениями, в том числе как управлять запускаемым в фоновом режиме приложением. В следующей главе мы углубимся в программирование MicroPython. Мы узнаем больше о специальных библиотеках, доступных в ваших IoT-проектах, предназначенных для работы на платах MicroPython.
Глава 5 Библиотеки MicroPython Теперь, когда мы понимаем, как писать код на Python и MicroPython, пришло время взглянуть на вспомогательные библиотеки, входящие в прошивку. Как вы увидите, библиотеки, доступные в MicroPython, аналогичны библиотекам Python. Фактически библиотеки встроенного ПО (иногда называемые интерфейсом прикладного программирования API, или API встроенного ПО) включают в себя множество библиотек Python. Есть несколько заметных исключений для стандартных библиотек, когда в MicroPython имеется эквивалентная библиотека, но она была переименована, чтобы отличать ее от библиотеки Python. В этом случае объем библиотеки был либо уменьшен за счет удаления менее часто используемых функций, либо каким-то образом модифицирован для соответствия платформе MicroPython – и все это для экономии места в памяти. Существуют также библиотеки, специфичные для MicroPython, и оборудование, обеспечивающее функциональные возможности, которые могут присутствовать или отсутствовать в некоторых общих выпусках Python. Эти библиотеки предназначены для упрощения работы с конкретными микроконтроллерами и аппаратным обеспечением. Таким образом, в прошивке есть три типа библиотек: стандартные и в основном такие же, как в Python, специфичные для MicroPython и специфичные для оборудования. Существует еще один тип библиотек, чаще всего называемый пользовательским. Пользовательские библиотеки – это библиотеки (API), которые мы создаем сами, которые можем развернуть на конкретной плате и тем самым сделать функциональность доступной для всех наших проектов. В этой главе вы найдете обзор всех типов библиотек. ПРИМЕЧАНИЕ Название «модуль» или «модуль кода» (code module) иногда используется для обозначения библиотеки; однако модуль обычно представляет собой один файл, содержащий текст кода, а библиотека может состоять из множества файлов. Таким образом, можно называть библиотеку модулем, если библиотека представляет собой один файл (модуль). Другое слово, иногда используемое для обозначения библиотеки, – «пакет», что подразумевает наличие нескольких файлов-модулей. С этим термином вы тоже можете столкнуться.
Встроенные и стандартные библиотеки  169 Вместо того чтобы просто переписать или скопировать существующую документацию1, мы разместим обзоры библиотек в виде кратких справочных таблиц, которые вы можете использовать, чтобы ознакомиться с тем, что доступно. Простое знание того, что доступно, часто может ускорить разработку и сделать ваши программы эффективнее, поскольку вам не придется изобретать что-то заново или тратить много времени на поиск библиотеки, которая может (или не может) вам понадобиться. Однако есть несколько библиотек, которые имеют решающее значение для обучения разработке IoT-проектов на MicroPython. Мы рассмотрим их более подробно с помощью фрагментов кода, которые помогут вам их изучить. Итак, хотя эта глава предназначена для ознакомления вас с наиболее распространенными библиотеками, она также является справочным руководством для более глубокого понимания прошивки MicroPython со ссылками на официальную документацию. Давайте начнем с рассмотрения тех библиотек MicroPython, которые являются «стандартными» библиотеками Python. Встроенные и стандартные библиотеки Как вы знаете, MicroPython построен на Python. Была проделана большая работа по упрощению работы, чтобы большинство библиотек Python могли поместиться на чипе. Полученная прошивка MicroPython значительно меньше, чем Python на вашем ПК, но никоим образом не урезана. То есть MicroPython – это Python, и поэтому MicroPython имеет многие из тех же библиотек, что и Python. Некоторые могут назвать эти библиотеки «встроенными», но правильнее называть их «стандартными» библиотеками, поскольку эти библиотеки такие же, как и в Python. Точнее, у них те же классы с теми же функциями, что и в Python. Таким образом, вы можете написать сценарий на своем ПК и выполнить его там, а затем выполнить тот же сценарий в неизмененном виде на своей плате MicroPython. Отлично! Как вы можете догадаться, это очень помогает при разработке сложного проекта. Напомним, мы видели эту технику на примерах из предыдущей главы. Там мы видели, что можно разработать часть вашего скрипта с использованием стандартных библиотек и отладить ее на своем ПК. То есть как только они заработают правильно, вы можете перейти к следующей части, для которой требуются библиотеки MicroPython или аппаратные библиотеки. Это потому, что разработка на ПК намного проще. Нам не нужно включать плату, подключать ее к Wi-Fi и т. д., чтобы программа заработала. Плюс ПК намного быстрее. Применяя эту практику, мы потенциально можем избавить себя от некоторых разочарований, гарантируя, что «стандартные» части нашего проекта работают правильно. 1 Совершенно плачевная практика. – Прим. авт.
170  Библиотеки MicroPython В этом разделе мы рассмотрим стандартные библиотеки Python, начиная с краткого обзора того, что доступно, а затем подробно о том, как использовать некоторые из наиболее распространенных библиотек. СОВЕТ Дополнительные сведения о стандартных библиотеках MicroPython см. на странице http://docs.micropython.org/en/latest/library/index.html#python-standard-libraries-and-micro-libraries. Обзор Стандартные библиотеки MicroPython содержат объекты, которые вы можете использовать для выполнения математических функций, работы с программными структурами, работы с переносимыми документами (или хранилищем документов) через JSON, взаимодействия с операционной системой и другими системными функциями и даже выполнения расчетов в реальном времени. Таблица 5.1 содержит список текущих стандартных библиотек MicroPython. Первый столбец – это имя, которое мы используем в нашем операторе импорта, второй – краткое описание, а третий содержит ссылку на онлайн-документацию. Как вы увидите, библиотеки MicroPython могут многое предложить. Таблица 5.1. Стандартные библиотеки Python в MicroPython Имя array asyncio Описание Работа с массивами Планировщик асинхронного ввода-вывода binascii Преобразования двоичный код / ASCII builtins Встроенные функции и исключения cmath Общая математика collections Работа с контейнерами errno Распространенные системные ошибки gc Работа со сборщиком мусора hashlib Библиотека хеширования heapq Библиотека очереди кучи io Работа с потоками ввода-вывода json Кодирование и декодирование JSON Документация http://docs.micropython.org/en/latest/library/array.html http://docs.micropython.org/en/latest/library/asyncio.html http://docs.micropython.org/en/latest/library/binascii.html http://docs.micropython.org/en/latest/library/builtins.html http://docs.micropython.org/en/latest/library/cmath.html http://docs.micropython.org/en/latest/library/collections. html http://docs.micropython.org/en/latest/library/errno.html http://docs.micropython.org/en/latest/library/gc.html http://docs.micropython.org/en/latest/library/hashlib.html http://docs.micropython.org/en/latest/library/heapq.html http://docs.micropython.org/en/latest/library/io.html http://docs.micropython.org/en/latest/library/json.html
Встроенные и стандартные библиотеки  171 Таблица 5.1 (окончание) Имя math os random re select socket Ssl struct sys time zlib _thread Описание Математические функции Службы операционной системы Генератор случайных чисел Работа с регулярными выражениями Работа с событиями потока ввода-вывода Библиотека сокетов Библиотека SSL/TLS Работа с типами данных Системные функции Функции времени и даты Распаковка архива zlib Поддержка многопоточности Документация http://docs.micropython.org/en/latest/library/math.html http://docs.micropython.org/en/latest/library/os.html http://docs.micropython.org/en/latest/library/random.html http://docs.micropython.org/en/latest/library/re.html http://docs.micropython.org/en/latest/library/select.html http://docs.micropython.org/en/latest/library/socket.html http://docs.micropython.org/en/latest/library/ssl.html http://docs.micropython.org/en/latest/library/struct.html http://docs.micropython.org/en/latest/library/sys.html http://docs.micropython.org/en/latest/library/time.html http://docs.micropython.org/en/latest/library/zlib.html http://docs.micropython.org/en/latest/library/_thread.html Большинство представленных здесь библиотек являются общими для большинства плат MicroPython, но у поставщика платы могут быть доступны библиотеки для конкретного оборудования, которые позволяют использовать специальные аппаратные функции, такие как встроенные датчики. Если у вас есть сомнения, всегда обращайтесь к документации MicroPython для вашей платы. СОВЕТ Формат оператора импорта позволяет нам указывать каталоги. Например, если мы используем команду import mylibs.io, MicroPython попытается найти библиотеку (модуль кода) с именем io.py в папке mylibs. Это может повлиять на использование модулей без префикса u1. Если мы использовали import io и io.py не является модулем кода, он будет использовать io в качестве имени папки и, если она существует, искать модули в этой папке. Таким образом, вы можете попасть в беду, если будете использовать имена папок, совпадающие с именами библиотек Python. Далее мы рассмотрим некоторые из наиболее часто используемых стандартных библиотек и увидим несколько примеров кода для каждой. Но сначала следует обсудить две категории стандартных функций. 1 О префиксе u см. примечание в разделе «Общие стандартные библиотеки» далее. – Прим. перев.
172  Библиотеки MicroPython Интерактивная справка для библиотек Малоизвестная функция help() может оказаться очень полезной при изучении биб­ лиотек MicroPython. Вы можете использовать эту функцию в сеансе REPL для получения информации о библиотеке, но сначала обязательно импортируйте библиотеку. Ниже показан фрагмент вывода справки по библиотеке os: >>> import os >>> help(os) object <module 'uos'> is of type module __name__ -- uos uname -- <function> urandom -- <function> chdir -- <function> getcwd -- <function> listdir -- <function> mkdir -- <function> remove -- <function> rename -- <function> rmdir -- <function> stat -- <function> statvfs -- <function> unlink -- <function> dupterm -- <function> ilistdir -- <function> mount -- <function> umount -- <function> VfsFat -- <class 'VfsFat'> VfsLfs2 -- <class 'VfsLfs2'> Обратите внимание, что мы видим имена всех функций и, если они есть, констант. Это может оказаться реальной помощью при изучении библиотек и того, что они содержат. Попробуйте это! Общие стандартные библиотеки Теперь давайте посмотрим на примеры некоторых наиболее часто используемых стандартных библиотек. Далее следует лишь пример того, что вы можете сделать с каждой из них. Полное описание всех возможностей см. в онлайн-документации. Как и большинство библиотек MicroPython, общие стандартные библиотеки могут быть подмножествами полной реализации Python 3 (CPython1). 1 CPython – эталонная реализация языка Python. См. https://ru.wikipedia.org/wiki/CPython. – Прим. перев.
Встроенные и стандартные библиотеки  173 ПРИМЕЧАНИЕ Некоторые реализации MicroPython называют библиотеки, сущест­ вующие в Python, добавляя к имени букву u1. Например, uio – это порт MicroPython биб­ лиотеки Python io. Большинство портированных библиотек аналогичны своим аналогам в Python, поэтому вам не нужно добавлять к имени букву u. Однако могут возникнуть ситуации, когда вам может потребоваться использовать именно библиотеку MicroPython (указав префикс u). Библиотека sys Библиотека sys (или usys) обеспечивает доступ к системе выполнения, такой как константы, переменные, параметры командной строки, потоки (stdout, stdin, stderr) и многое другое. Большинство функций библиотеки представляют собой константы или списки. Доступ к потокам возможен напрямую, но обычно мы используем функцию print(), которая по умолчанию отправляет данные в поток stdout. Наиболее часто используемые функции в этой библио­ теке включают следующие:   sys.argv: список аргументов, передаваемых сценарию из командной строки;   sys.exit(r): выход из программы, возвращающий значение r вызывающей стороне;   sys.modules: список загруженных (импортированных) модулей;   sys.path: список путей для поиска модулей, можно изменить;   sys.implementation: список информации о поставщике для версии MicroPython;   sys.platform: отображение информации о платформе, такой как Linux, MicroPython и т. д.;   sys.byteorder: порядок байтов (с прямым или с обратным порядком байтов)2;   sys.maxsize: максимальный размер (значение), хранящееся в целочисленном формате;   sys.stderr: стандартный поток ошибок;   sys.stdin: стандартный поток ввода;   sys.stdout: стандартный поток вывода;   sys.version: версия Python, выполняющаяся в данный момент;   sys.version_info: номер версии Python в кортеже;   sys.exit: функция прекращения выполнения. В лис­тин­ге 5.1 показаны некоторые из этих переменных и функция exit(). 1 2 Префикс u в имени библиотек MicroPython не следует путать с одноименным префиксом, который использовался в Python 2 для указания Unicode-строки (в отличие от обычной ASCII-строки в однобайтовой кодировке). В Python 3 этот префикс в приложении к строкам не используется. – Прим. перев. www.baeldung.com/cs/big-endian-vs-little-endian. – Прим. авт. То же на русском языке https://ru.wikipedia.org/wiki/Порядок_байтов. – Прим. перев.
174  Библиотеки MicroPython Листинг 5.1  Демонстрация возможностей библиотеки sys # MicroPython for the IoT Second Edition - Chapter 5 # Example use of the sys library import sys print("Modules loaded: " , sys.modules) sys.path.append("/sd") print("Path: ", sys.path) sys.stdout.write("Platform: ") sys.stdout.write(sys.platform) sys.stdout.write("\n") sys.stdout.write("Version: ") sys.stdout.write(sys.version) sys.stdout.write("\n") sys.exit(1) Обратите внимание, что мы начинаем с оператора импорта, а после этого можем распечатать константы и переменные из библиотеки sys, используя функцию print(). Мы также видим, как можно добавить путь для поиска с помощью функции sys.path.append(). Это очень полезно, если мы создаем собственные каталоги для размещения нашего кода. Без этого добавления оператор импорта завершится ошибкой, если модуль кода не находится в каталоге lib. В конце примера показано, как использовать поток stdout для вывода данных на экран. Обратите внимание, что вы должны представить команду возврата каретки (\n), чтобы перевести вывод на новую строку, тогда как функция print() позаботится об этом за нас. Ниже показаны результаты запуска этого сценария на плате MicroPython: Modules loaded: {'rp2': <module 'rp2' from 'rp2.py'>} Path: ['', '.frozen', '/lib', '/sd'] Platform: rp2 Version: 3.4.0; MicroPython v1.20.0 on 2023-04-26 Обратите внимание, что мы видим данные так, как ожидали, и что этот пример выполняется на модуле Raspberry Pi Pico ('rp2'). Также обратите внимание на отсутствие аргументов командной строки – потому что мы использовали оператор импорта. Однако если бы мы запустили код на нашем ПК, представив аргументы командной строки, мы бы их увидели. Ниже показаны результаты запуска этого сценария на ПК: $ python ./sys_example.py Modules loaded: {'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (builtin)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'winreg': <module 'winreg' (builtin)>, '_io': <module '_io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, ..., '/ sd'] Platform: win32 Version: 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)]
Встроенные и стандартные библиотеки  175 Библиотека io Библиотека io (или uio) содержит дополнительные функции для работы с потоками и потоковыми файлами как объектами. Существует единственная функция с именем io.open(), которую можно использовать для открытия файлов (хотя большинство людей используют встроенную функцию с именем open()), а также классы для потоков байтов и строк. Фактически классы имеют схожие файловые функции, такие как read(), write(), seek(), flush(), close(), а также функцию getvalue(), которая возвращает содержимое буфера потока, содержащего данные. Давайте посмотрим на пример. В примере ниже мы сначала открываем новый файл для записи и записываем в него массив байтов. Используемый метод заключается в передаче шестнадцатеричных значений для каждого байта в функцию write(). Когда вы считываете данные с датчиков, они обычно имеют двоичную форму (байт или строка байтов). Вы обозначаете байт с помощью escape-символа \x, как показано в примере. После записи данных в файл мы потом читаем файл по одному байту, передавая это число в функцию read(). Затем выводим считанные значения, возвращаемые вызовом read(1), в необработанном виде, в десятичной и шестнадцатеричной формах. В лис­тин­ге 5.2 показано, как читать файл в двоичном режиме. Записанные байты содержат секретное слово (которое скрыто за шестнадцатеричными значениями) – вы его видите? Листинг 5.2  Демонстрация возможностей библиотеки uio # MicroPython for the IoT - Chapter 5 # Example use of the io library import io # Create the binary file fio_out = io.open('data.bin', 'wb') fio_out.write("\x5F\x9E\xAE\x09\x3E\x96\x68\x65\x6C\x6C\x6F") fio_out.write("\x00") fio_out.close() # Read the binary file and print out the results in hex and char. fio_in = io.open('data.bin', 'rb') print("Raw,Dec,Hex from file:") byte_val = fio_in.read(1) # read a byte while byte_val: print(byte_val, ",", ord(byte_val), hex(ord(byte_val))) byte_val = fio_in.read(1) # read a byte fio_in.close() В лис­тин­ге 5.3 показаны выходные результаты при запуске на плате MicroPython, но вы также можете запустить код на своем ПК с тем же результатом. Листинг 5.3  Демонстрация возможностей библиотеки io Raw,Dec,Hex from file: b'_' , 95 0x5f b'\xc2' , 194 0xc2
176  Библиотеки MicroPython b'\x9e' , 158 0x9e b'\xc2' , 194 0xc2 b'\xae' , 174 0xae b'\t' , 9 0x9 b'>' , 62 0x3e b'\xc2' , 194 0xc2 b'\x96' , 150 0x96 b'h' , 104 0x68 b'e' , 101 0x65 b'l' , 108 0x6c b'l' , 108 0x6c b'o' , 111 0x6f b'\x00' , 0 0x0 Если вам интересно, как выглядит файл, вы можете использовать такую утилиту, как hexdump, для печати содержимого. Теперь видите скрытое сообщение? $ hexdump -C data.bin 00000000 5f 9e ae 09 3e 96 68 65 6c 6c 6f 00 |_...>.hello.| 0000000c Библиотека json Библиотека json (или ujson) – одна из тех библиотек, которые вы, вероятно, будете часто использовать при работе с данными в проектах IoT. Она обеспечивает кодирование и декодирование документов JavaScript Object Notation (JSON). Это связано с тем, что многие доступные службы интернета вещей либо требуют, либо могут обрабатывать документы JSON. Таким образом, вам следует подумать о том, чтобы выработать привычку форматировать данные в формате JSON, чтобы облегчить интеграцию с другими системами. В биб­ лиотеке реализованы следующие функции, которые можно использовать для работы с документами JSON:   json.dump(obj,stream): возвращает строку, декодированную из объекта JSON, в поток;   json.dumps(obj): возвращает строку, декодированную из объекта JSON;   json.loads(stream): анализирует строку JSON и возвращает объект JSON. Вызовет ошибку, если отформатировано неправильно;   json.load(str): анализирует содержимое документа JSON. Вызовет ошибку, если отформатировано неправильно. Напомним, в предыдущей главе мы видели краткий пример документов JSON. Этот пример был написан исключительно для ПК, но небольшое изменение позволяет запустить его на плате MicroPython. Давайте посмотрим на аналогичный пример. В лис­тин­ге 5.4 показан пример использования биб­ лиотеки ujson. Листинг 5.4  Демонстрация возможностей библиотеки json # MicroPython for the IoT Second Edition - Chapter 5 # Example use of the json library
Встроенные и стандартные библиотеки  177 import json # Prepare a list of JSON documents for pets by converting JSON to a dictionary vehicles = [] vehicles.append(json.loads('{"make":"Chevrolet", "year":2015, "model":"Silverado", "color":"Pull me over red", "type":"pickup"}')) vehicles.append(json.loads('{"make":"Yamaha", "year":2009, "model":"R1", "color":"Blue/ Silver", "type":"motorcycle"}')) vehicles.append(json.loads('{"make":"SeaDoo", "year":1997, "model":"Speedster", "color":"White", "type":"boat"}')) vehicles.append(json.loads('{"make":"TaoJen", "year":2013, "model":"Sicily", "color":"Black", "type":"Scooter"}')) # Now, write these entries to a file. Note: overwrites the file json_file = open("my_vehicles.json", "w") for vehicle in vehicles: json_file.write(json.dumps(vehicle)) json_file.write("\n") json_file.close() # Now, let's read the list of vehicles and print out their data my_vehicles = [] json_file = open("my_vehicles.json", "r") for vehicle in json_file.readlines(): parsed_json = json.loads(vehicle) my_vehicles.append(parsed_json) json_file.close() # Finally, print a summary of the vehicles print("Year Make Model Color") for vehicle in my_vehicles: print(vehicle['year'],vehicle['make'],vehicle['model'], vehicle['color']) Ниже показаны результаты запуска этого сценария на MicroPython: Year 2015 2009 1997 2013 Make Model Color Chevrolet Silverado Pull me over red Yamaha R1 Blue/Silver SeaDoo Speedster White TaoJen Sicily Black Библиотека os Библиотека os (или uos) реализует набор функций для работы с базовой операционной системой. Некоторые функции могут быть вам знакомы, если вы писали программы для своего ПК. Большинство функций позволяют проводить операции с файлами и каталогами. Ниже перечислены некоторые из наиболее часто используемых функций:   os.chdir(path): изменить текущий каталог;   os.getcwd(): возвращает текущий рабочий каталог;   os.listdir([dir]): список содержимого каталога [dir] или текущего каталога, если параметр [dir] отсутствует;
178  Библиотеки MicroPython      os.mkdir(путь): создать новый каталог; os.remove(путь): удалить файл; os.rmdir(path): удалить каталог; os.rename(old_path, new_path): переименовать файл; os.stat(path): получить статус файла или каталога. В примере далее мы демонстрируем, как изменить рабочий каталог, чтобы упростить операторы импорта. Для кода этого примера требуется файл json_example.py, который мы видели ранее, помещенный в папку на плате MicroPython с именем example. Таким образом, вы должны создать каталог example на своей плате MicroPython, а затем загрузить файл json_example.py в эту папку. См. главу 2 о том, как работать с файлами и платой MicroPython. СОВЕТ Для достижения наилучших результатов скопируйте все файлы исходного кода для этой главы в папку примеров на плате MicroPython. Мы также демонстрируем, как создать новый каталог, переименовать его, создать файл в новом каталоге, составить список каталогов и, наконец, очис­ тить (удалить) изменения. В лис­тин­ге 5.5 показан пример работы с функ­ циями библиотеки os. Листинг 5.5  Демонстрация возможностей библиотеки os # MicroPython for the IoT Second Edition - Chapter 5 # Example use of the os library # Note: change os to os to run it on your PC! import sys import os # Create a method to display files in directory def show_files(): files = os.listdir() sys.stdout.write("\nShow Files Output:\n") sys.stdout.write("\tname\tsize\n") for file in files: stats = os.stat(file) # Print a directory with a "d" prefix and the size is_dir = True if stats[0] > 16384: is_dir = False if is_dir: sys.stdout.write("d\t") else: sys.stdout.write("\t") sys.stdout.write(file) if not is_dir: sys.stdout.write("\t") sys.stdout.write(str(stats[6])) sys.stdout.write("\n") # List the current directory show_files()
Встроенные и стандартные библиотеки  179 # Change to the examples directory os.chdir('examples') show_files() # Show how you can now use the import statement with the current dir print("\nRun the ujson_example with import ujson_example after chdir()") import json_example # Create a directory os.mkdir("test") show_files() # Remove the directory os.rmdir('test') show_files() Хотя этот пример немного длинный, в нем показаны некоторые интересные приемы. Обратите внимание, что мы создали функцию для распечатки списка каталогов, а не для распечатки списка возвращенных файлов. Мы также проверяли статус файла, чтобы определить, является файл каталогом или нет, и если да, то печатали d (directory), чтобы указать, что имя относится к каталогу. Мы также использовали поток stdout для управления форматированием с помощью символов табуляции (\t) и новой строки (\n). Мы продемонстрируем, как использовать каталог изменений, чтобы упрос­ тить использование оператора импорта. Поскольку мы изменили каталог на examples, команда import json_example сначала попытается найти этот модуль (библиотеку) в текущем каталоге. Это хороший трюк, который вы можете использовать в своих проектах для развертывания кода на вашей плате в отдельном каталоге. Это означает, что вы можете использовать его для развертывания нескольких проектов на вашей плате, размещая каждый в своем собственном каталоге. Теперь давайте посмотрим результат. В лис­тин­ге 5.6 показаны результаты работы этого скрипта на MicroPython. Уделите несколько минут и просмот­ рите выходные данные, чтобы увидеть, как работают функции. Также обратите внимание, что показаны выходные данные примера JSON, поскольку мы импортировали этот код в середине скрипта. Отлично! Листинг 5.6  Демонстрация возможностей библиотеки os (результат) Show Files Output: name boot.py data.bin d examples my_vehicles.json roman.py roman_numerals.py Show Files Output: name size 17 15 377 612 1607 size
180  Библиотеки MicroPython exception_example.py import_errors.py io_example.py json_example.py my_vehicles.json os_example.py sys_example.py time_example.py 520 272 567 1326 377 1169 366 1420 Run the ujson_example with import ujson_example after chdir() Year Make Model Color 2015 Chevrolet Silverado Pull me over red 2009 Yamaha R1 Blue/Silver 1997 SeaDoo Speedster White 2013 TaoJen Sicily Black Show Files Output: name exception_example.py import_errors.py io_example.py json_example.py my_vehicles.json os_example.py sys_example.py d test time_example.py Show Files Output: name exception_example.py import_errors.py io_example.py json_example.py my_vehicles.json os_example.py sys_example.py time_example.py size 520 272 567 1326 377 1169 366 1420 size 520 272 567 1326 377 1169 366 1420 Библиотека time Библиотека time (или utime) – еще одна популярная библиотека, используемая во многих проектах. Библиотека используется для получения текущего времени и даты, а также, например, для расчета разницы во времени. Обратите внимание, что некоторые функции работают только с часами реального времени (RTC), установленными в качестве аппаратного модуля, другие – через сетевой сервер времени (для получения более подробной информации см. главу 6). Ниже перечислены некоторые наиболее часто используемые функции, связанные с вставкой задержек в наши сценарии. Задержки полезны, когда нам нужно приостановить обработку, чтобы датчик мог прочитать или отправить данные либо дождаться данных из других источников. Другое
Встроенные и стандартные библиотеки  181 распространенное использование этой библиотеки – запись даты и времени события, например чтения данных датчика:   time.sleep(seconds): переводит плату в спящий режим на указанное количество секунд;   time.sleep_ms(ms): переводит плату в спящий режим на указанное количество миллисекунд;   time.sleep_us(us): переводит плату в спящий режим на указанное количество микросекунд. Давайте посмотрим краткий пример того, как использовать временные задержки. Здесь мы используем случайную функцию для сна в течение случайного периода и предоставления случайных значений. В лис­тин­ге 5.7 показан код применения библиотеки time. Подробнее о времени и часах реального времени мы поговорим в главе 6. Листинг 5.7  Демонстрация функций сна библиотеки time # MicroPython for the IoT Second Edition - Chapter 5 # Example use of the time library # Note: This example only works on MicroPython and will not run on your PC. import machine import sys import time from random import randint # Init with default time and date synchTime = time.mktime((2023, 7, 16, 15, 18, 11, 6, 190)) delta = synchTime - int(time.time()) def now(): return time.localtime(time.time() + delta) # Format the time (epoch) for a better view def format_time(tm_data): # Use a special shortcut to unpack tuple: *tm_data return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_ data) # Generate a random number of rows from 0-25 num_rows = randint(0,25) # Print a row using random seconds, milleseconds, or microseconds # to simulate time. print("Datetime Value") print("------------------- --------") for i in range(0,num_rows): # Generate random value for our sensor value = randint(0,9999999) / 10000 # Wait a random number of seconds for time time.sleep(randint(1,15)) # sleep up to 10 seconds print("{0} {1:0>{width}.4f}".format(format_time(now()), value, width=8)) Обратите внимание, что в этом примере происходит нечто большее, чем просто ожидание случайного количества секунд. Здесь показано, как это сде-
182  Библиотеки MicroPython лать, а также как работать с данными времени и данными форматирования. Обычно это необходимо делать при регистрации данных. Давайте немного пройдемся по этому вопросу. Мы создаем две функции: одну для генерации текущей даты и времени, а другую для форматирования даты и времени в удобном для пользователя формате. Функция format_time() возвращает строку, представляющую дату и время. Здесь мы видим некоторые расширенные параметры форматирования даты с использованием правильных цифр для каждой части, в частности четыре цифры для года и по две для месяца, дня, часа, минут и секунд. Опция :0>2 сообщает функции format(), что значения должны располагаться по двум позициям (цифрам) с ведущими нулями. Обратите внимание на последний оператор print(): здесь мы используем другой трюк для передачи длины запи­си с помощью именованного параметра (width). Наконец, мы переходим к примеру кода, в котором генерируем случайное количество строк от 0 до 25, затем выполняем цикл для этих итераций, генерирующих случайное значение (0–999,9999), а потом ждем (переходим в режим сна) случайное количество секунд (1–10), далее выводим новую дату, время и значение. Это моделирует типичный цикл, который мы будем использовать для считывания данных с датчиков. Поскольку большинству датчиков требуется время для считывания значения, функция сна позволяет нам подождать в течение этого времени1. Теперь, когда мы знаем больше о том, что делает этот код, давайте посмот­ рим на пример того, как он работает. Ниже показаны результаты выполнения этого кода на MicroPython: Datetime ------------------2023-07-16 15:18:12 2023-07-16 15:18:19 2023-07-16 15:18:22 2023-07-16 15:18:23 2023-07-16 15:18:37 2023-07-16 15:18:42 2023-07-16 15:18:54 2023-07-16 15:19:02 2023-07-16 15:19:04 2023-07-16 15:19:12 2023-07-16 15:19:22 2023-07-16 15:19:26 2023-07-16 15:19:41 2023-07-16 15:19:42 1 Value -------003.5471 429.3827 987.4546 113.1418 239.6263 338.2216 315.0221 086.1253 533.3747 602.3252 394.2683 632.7906 094.1397 149.6111 Кроме того, имейте в виду, что этот интервал, называемый частотой дискретизации, также должен иметь смысл в контексте проекта. Замеры температуры окружающей среды при контролировании климата 30 раз в секунду могут генерировать значительный объем данных, которые бесполезны, поскольку температура меняется гораздо медленнее. – Прим. авт.
Встроенные и стандартные библиотеки  183 2023-07-16 2023-07-16 2023-07-16 2023-07-16 2023-07-16 2023-07-16 2023-07-16 15:19:54 15:19:59 15:20:08 15:20:18 15:20:23 15:20:35 15:20:41 281.4139 136.0484 697.6131 164.4964 818.4449 439.5562 060.8383 Существуют также встроенные функции, которые не являются частью какой-либо конкретной библиотеки, а также исключения, которые позволяют нам фиксировать ошибочные ситуации. Давайте рассмотрим их, прежде чем погрузиться в некоторые из наиболее часто используемых стандартных биб­ лиотек. Встроенные функции и классы Python поставляется со множеством встроенных функций: таких, которые вы можете вызывать непосредственно из сценария, не импортируя их. Сущест­ вует также множество классов, которые можно использовать для определения переменных, работы с данными и многого другого. Это объекты, поэтому вы можете использовать их для хранения данных и выполнения операций с данными. Некоторые из них мы уже видели в примерах. Давайте посмотрим на некоторые основные встроенные функции и классы. Таблица 5.2 содержит краткое описание каждого из них. Дополнительную информацию можно найти по адресу https://docs.python.org/3/library/functions. html. Хотя это документация по Python, она применима и к MicroPython. Классы обозначаются словом «class»; остальное – функции. Таблица 5.2. Встроенные функции и классы MicroPython Название abs(x) all(iterable) any(iterable) bin(x) class bool([x]) Описание Возвращает абсолютное значение числа x Возвращает true, если все элементы итерируемого объекта истинны (или если итерируемый объект пуст) Возвращает true, если какой-либо элемент итерируемого объекта имеет значение true Преобразование целого числа в двоичную строку Возвращает логическое значение, то есть одно из значений true или false class bytearray([source[, Возвращает новый массив байтов encoding[, errors]]]) class bytes([source[, Возвращает новый объект «байты», который представляет encoding[, errors]]]) собой неизменяемую последовательность целых чисел в диапазоне 0 <= x < 256 callable(object) Возвращает true, если аргумент объекта представляется вызываемым (callable), и false, если нет chr(i) Возвращает символ, код которого в Юникоде равен i
184  Библиотеки MicroPython Таблица 5.2 (продолжение) Название classmethod(function) class complex([real[, imag]]) delattr(obj, name) class dict() dir([object]) divmod(a,b) enumerate(iterable, start=0) eval(expression, globals=None, locals=None) exec(object[, globals[, locals]]) filter(function, iterable) class float([x]) class frozenset([iterable]) getattr(object, name[, default]) globals() hasattr(object, name) hash(object) hex(x) id(object) input([prompt]) class int(x) Описание Возвращает метод класса для функции Возвращает комплексное число со значением real + imag*1j или преобразует строку либо число в комплексное число Это родственник setattr(). Аргументами являются объект и строка. Строка должна быть именем одного из атрибутов объекта Создает новый словарь Без аргументов возвращает список имен в текущей локальной области. С аргументом попытается вернуть список допустимых атрибутов для заданного объекта Получает два (некомплексных) числа в качестве аргументов и возвращает пару чисел, состоящую из их частного и остатка при использовании целочисленного деления Возвращает перечисляемый объект. Iterable должен быть последовательностью, итератором или каким-либо другим объектом, поддерживающим итерацию Определяет выражение, используя глобальные и локальные переменные как словари в локальном пространстве имен Выполняет набор операторов или объектов Python, используя глобальные и локальные переменные в качестве словарей в локальном пространстве имен Создает итератор из тех элементов итерируемого объекта, для которых функция возвращает результат true Возвращает число с плавающей запятой, созданное из числа или строки Возвращает новый объект frozenset, возможно, с элементами, взятыми из итерируемого объекта Возвращает значение именованного атрибута объекта. Имя name должно быть строкой Возвращает словарь, представляющий текущую глобальную таблицу символов Аргументами являются объект и строка. Результатом является true, если строка является именем одного из атрибутов объекта, и false, если нет Возвращает хеш-значение для объекта (если оно существует). Хеш-значения являются целыми числами Преобразование целого числа в шестнадцатеричную строку нижнего регистра с префиксом «0x» Возвращает «идентификатор» объекта Если аргумент prompt присутствует, он записывается в стандартный вывод без завершающего символа новой строки. Затем функция считывает строку из входных данных, преобразует ее в строку (удаляя завершающий символ новой строки) и добавляет ее Возвращает целочисленный объект, созданный из числа или строки x, или возвращает 0, если аргументы не указаны
Встроенные и стандартные библиотеки  185 Таблица 5.2 (продолжение) Название isinstance(object, classinfo) issubclass(class, classinfo) Описание Возвращает true, если аргумент объекта является экземпляром аргумента classinfo или его (прямого, косвенного или виртуального) подкласса Возвращает true, если класс является подклассом (прямым, косвенным или виртуальным) classinfo iter(object[, sentinel]) Возвращает объект-итератор len(s) Возвращает длину (количество элементов) объекта class list([iterable]) Последовательность списка locals() Обновить и вернуть словарь, представляющий текущую локальную таблицу символов map(function, iterable, Возвращает итератор, который применяет функцию …) к каждому элементу итерации, получая результаты max([iterable|arg*]) Возвращает самый большой элемент в итерации или самый большой из двух или более аргументов class memoryview(obj) Возвращает объект «memory view», созданный на основе данного аргумента min([iterable|arg*]) Возвращает наименьший элемент в итерации или наименьший из двух или более аргументов next(iterator[, default]) Получите следующий элемент из итератора, вызвав его метод __next__() class objectO Возвращает новый объект object без свойств. Object является основой для всех классов oct(x) Преобразование целого числа в восьмеричную строку open(file, mode='r', Открывает файл и возвращает соответствующий файловый buffering=-1, объект. Используйте close(), чтобы закрыть файл encoding=None, errors=None, newline=None, closefd=True, opener=None) ord(c) Получает строку, представляющую один символ Unicode, возвращает целое число, представляющее код Unicode этого символа pow(x, y[, z]) Возвращает x в степени y; если z присутствует, вернуть x в степень y по модулю z (вычисляется более эффективно, чем pow(x, y) % z) print(*objects, sep=' ', Вывести объекты в файл текстового потока, разделенные end='\n', file=sys. разделителем sep, за которым следуют конец строки, файл stdout, flush=False) и ограничитель flush; если он присутствует, его необходимо указать в качестве аргументов ключевого слова class property(fget=None, Возвращает атрибут свойства fset=None, fdel=None, doc=None) range([stop|[start, Диапазон последовательности stop[, step]]]) repr(object) Возвращает строку, содержащую печатное представление объекта reversed(seq) Возвращает обратный итератор
186  Библиотеки MicroPython Таблица 5.2 (окончание) Название round(number[, ndigits]) Описание Возвращает число, округленное до точности n цифр после десятичной точки class set([iterable]) Возвращает новый объект набора, возможно, с элементами, взятыми из итерируемого объекта setattr(object, name, Это аналог getattr(). Аргументами являются объект, строка value) и произвольное значение class slice(start, stop[, Возвращает объект выборки, представляющий набор step]) индексов, заданных диапазоном от start до stop с шагом step sorted(iterable[, key][, Возвращает новый отсортированный список из элементов reverse]) в итерации staticmethod(function) Возвращает статический метод для функции class str(object) Возвращает строковую версию объекта sum(iterable[, start]) Суммирование начинается с элементов итерации слева направо и возвращает сумму super([type[, object-or- Возвращает прокси-объект, который делегирует вызовы type]]) функций родительскому или родственному классу типа class tuple([iterable]) Последовательность типа кортеж type(object) Возвращает тип объекта zip(*iterables) Создает итератор, объединяющий элементы из каждой итерации Вам следует просмотреть эту таблицу и изучить ссылки на те позиции, которые кажутся вам интересными, а также обращаться к этой таблице при разработке своих проектов, чтобы вы могли использовать наиболее подходящую функцию или класс. Вы можете быть удивлены, сколько всего «встроено». Однако еще раз всегда проверяйте документацию выбранной вами платы MicroPython на предмет новейших функций и классов, доступных в вашей прошивке. СОВЕТ Дополнительную информацию о встроенных функциях и классах см. на странице https://docs.python.org/3/library/functions.html. Для получения последнего списка встроенных функций и классов MicroPython см. http://docs.micropython.org/en/latest/ library/builtins.html. Давайте посмотрим пример использования одного из классов – словаря dictionary. Ниже показано, как мы можем использовать встроенный класс для создания переменной типа dict() и последующего ее использования. Поскольку этот класс является частью встроенной функциональности, пример будет работать как на Python, так и на MicroPython. >>> >>> {} >>> >>> my_addr = dict() print(my_addr) my_addr['street'] = '123 Main St' my_addr['city'] = 'Anywhere'
Встроенные и стандартные библиотеки  187 >>> my_addr['zip'] = 90125 >>> print(my_addr) {'city': 'Anywhere', 'street': '123 Main St', 'zip': 90125} >>> my_addr = {'street':'201 Cherry Tree Road', 'city':'Gobye', 'zip':12345} >>> print(my_addr) {'city': 'Gobye', 'street': '201 Cherry Tree Road', 'zip': 12345} Здесь мы видим, как можно использовать класс словаря для создания переменной этого типа. Мы можем увидеть это в первом вызове print(). Напомним, что синтаксис определения словаря представляет собой набор фигурных скобок. Далее мы добавляем значения в словарь, используя специальный синтаксис доступа к элементам. Наконец, мы переназначаем переменной новый набор данных, используя более знакомый синтаксис словаря. Теперь давайте поговорим о теме, о которой мы мало говорили, – об исключениях. Исключения являются частью встроенного модуля Python и могут оказаться очень важным приемом программирования, который необходимо использовать. Возможно, не сразу, но со временем вы оцените мощь и удобство использования исключений в своем коде. Исключения В Python (и MicroPython) есть мощный механизм, который помогает управлять событиями или фиксировать их при возникновении ошибок и выполнять специальный код для конкретной ошибки. Эта конструкция называется исключениями (exceptions). Исключения (ошибки), которые мы можем перехватить, называются классами исключений. Исключения используют специальный синтаксис, называемый оператором try (также называемый предложением, поскольку для формирования полного оператора требуется как минимум еще одно предложение), помогающий нам фиксировать ошибки по мере их возникновения. Исключения можно генерировать в любом месте кода с помощью функции raise(). То есть если что-то пойдет не так, программист может «вызвать» конкретное именованное исключение, а оператор try можно использовать для его перехвата с помощью предложения исключения. В табл. 5.3 показан список классов исключений, доступных в MicroPython, а также краткое описание того, когда (как) исключение может быть вызвано. Синтаксис оператора try показан ниже. Каждая часть конструкции называется предложением: try_stmt ::= try1_stmt | try2_stmt try1_stmt ::= "try" ":" code block ("except" [expression ["as" identifier]] ":" code block)+ ["else" ":" code block] ["finally" ":" code block] try2_stmt ::= "try" ":" code block "finally" ":" code block
188  Библиотеки MicroPython Таблица 5.3. Классы исключений MicroPython Класс исключения AssertionError AttributeError Exception ImportError IndexError KeyboardInterrupt KeyError MemoryError NameError NotImplementedError OSError RuntimeError StopIteration SyntaxError Описание Оператор assert() завершился сбоем Ошибочная ссылка на атрибут Базовый класс исключений Не удалось импортировать один или несколько модулей Индекс выходит за пределы допустимого диапазона Было нажато или смоделировано сочетание <CTRL>+<C> Введенный ключ отсутствует в списке ключей словаря Состояние «недостаточно памяти» Локальное или глобальное имя (переменная, функция и т. д.) не найдено Обнаружена абстрактная нереализованная функция Любая системная ошибка операционной системы Возможно, фатальная ошибка, возникшая при выполнении Следующая функция итератора сообщила, что в итерируемом объекте больше нет значений Обнаружена синтаксическая ошибка кода Обратите внимание, что здесь четыре предложения: try, except, else и finally. Предложение try – это место, куда мы помещаем наш код (блок кода) – одну или несколько строк кода, которые будут включены в перехват исключений. Может быть только одно try, else и finally, но вы можете иметь любое количество предложений, именующих класс исключения. Фактически except и else объединяются таким образом, что если исключение обнаружено при выполнении любой из строк кода в предложении try, оно будет искать предложения исключений, и если и только если предложение исключений не встречается, оно выполнит предложение else. Предложение finally используется для выполнения, после того как все исключения обработаны и выполнены. Также обратите внимание, что существует две версии оператора: одна, содержащая одно или несколько исключений и, при необходимости, else и, наконец, другая, содержащая только предложения try и finally. Давайте рассмотрим один из способов использования этого оператора для обнаружения ошибок в нашем коде. Предположим, вы считываете данные с группы датчиков, и библиотеки (модули) для этих датчиков выдают ValueError, если считанное значение выходит за пределы диапазона или недействительно. Также может случиться так, что вам не нужны данные от некоторых датчиков, если один или несколько из них выйдут из строя. Итак, мы можем использовать код, подобный следующему, чтобы «попытаться» прочитать каждый из датчиков и, если есть ValueError, выдать предупреждение и продолжить работу, или если произойдет какая-то другая ошибка, пометить ее как ошибку чтения. Обратите внимание, что обычно мы не останавливаем программу на этом этапе; скорее, мы обычно регистрируем
Встроенные и стандартные библиотеки  189 это и продолжаем работу. Изучите следующее, пока не убедитесь, что исключения – это очень удобно: values = [] print("Start sensor read.") try: values.append(read_sensor(pin11)) values.append(read_sensor(pin12)) values.append(read_sensor(pin13)) values.append(read_sensor(pin17)) values.append(read_sensor(pin18)) except ValueError as err: print("WARNING: One or more sensors valued to read a correct value.", err) except: print("ERROR: fatal error reading sensors.") finally: print("Sensor read complete.") Другой способ использования исключений – это когда мы хотим импортировать модуль (библиотеку), но не уверены, присутствует ли она. Например, предположим, что существует модуль с именем piano.py, в котором есть функция с именем keys(), которую вы хотите импортировать, но этот модуль может быть, а может и не быть в системе. В этом случае у нас может быть другой код, который мы можем использовать вместо импортируемого, создав собственную версию keys(). Чтобы проверить, можно ли импортировать модуль, мы можем поместить импорт внутри блока try, затем определить, произошел ли сбой импорта, и предпринять соответствующие шаги: # Try to import the keys() function from piano. If not present, # use a simulated version of the keys() function. try: from piano import keys except ImportError as err: print("WARNING:", err) def keys(): return(['A','B','C','D','E','F','G']) print("Keys:", keys()) Если мы добавили такой код, а модуль отсутствовал, мы не только можем ответить предупреждающим сообщением, но также можем определить нашу собственную функцию, которая будет использоваться, если модуль отсутствует. Наконец, вы можете вызвать любое исключение, включая созданное собственными руками. Создание пользовательских исключений – сложная тема, но давайте посмотрим, как мы можем создавать исключения, поскольку мы можем захотеть сделать это, если напишем свои собственные библиотеки. Предположим, у вас есть блок кода, считывающий значения, но возможно, что значение выходит за пределы диапазона. То есть слишком большое для целого числа, слишком маленькое для ожидаемого допустимого диапазона
190  Библиотеки MicroPython значений и т. д. Вы можете вызвать ValueError, передав свое собственное сообщение об ошибке, следующим образом, с помощью оператора raise и допустимого объявления класса исключения: raise ValueError("ERROR: the value read from the sensor ({0}) is not in range.".format(val_ read)) Затем вы можете использовать оператор try, чтобы уловить это условие, поскольку знаете, что его возникновение возможно, и обработать его в своем коде. Например, если вы считываете данные, вы можете пропустить чтение и продолжить цикл. Однако если бы это исключение возникло при отсутствии операторов try, вы могли бы получить ошибку, подобную следующей: Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: ERROR: the value read from the sensor (-12) is not in range. Вы можете использовать методы, аналогичные показанным здесь, чтобы сделать код MicroPython более надежным и устойчивым к ошибкам. Более того, вы можете написать свой код, чтобы предвидеть ошибки и реагировать на них корректным и контролируемым образом. СОВЕТ Более подробно об исключениях Python см. https://docs.python.org/3/tutorial/ errors.html. Библиотеки MicroPython Существуют также библиотеки, созданные специально для системы MicroPython. Это библиотеки, призванные облегчить использование MicroPython на оборудовании. Подобно встроенным, существуют библиотеки MicroPython, которые применимы к любой плате, а также библиотеки, которые различаются от одной платы к другой. То есть в таких библиотеках есть тонкие различия, которые не позволяют им работать более чем на одной разновидности плат. Всегда обращайтесь к документации по прошивке вашей платы, чтобы получить полный список доступных библиотек MicroPython. Обзор Библиотеки MicroPython предоставляют функциональные возможности, специфичные для реализации Python в MicroPython. Есть библиотеки с функциями для работы напрямую с железом, системой MicroPython и сетью. В следующих разделах мы увидим примеры некоторых функций каждой из этих библиотек. Таблица 5.4 содержит список текущих библиотек MicroPython,
Библиотеки MicroPython  191 общих для большинства плат. Первый столбец – это имя, которое мы используем в нашем операторе импорта, второй – краткое описание, а третий содержит ссылку на онлайн-документацию. Таблица 5.4. Библиотеки, специфичные для MicroPython Имя bluetooth Описание Bluetoothбиблиотека btree Библиотека BTree cryptolib Криптографическая библиотека framebuf Кадровый буфер machine Аппаратные функции micropython Внутреннее устройство MicroPython neopixel Библиотека светодиодов NeoPixel (WS2812) network Сетевые функции uctypes Структурированные двоичные данные Документация http://docs.micropython.org/en/latest/library/bluetooth. html http://docs.micropython.org/en/latest/library/btree.html http://docs.micropython.org/en/latest/library/cryptolib.html http://docs.micropython.org/en/latest/library/framebuf.html http://docs.micropython.org/en/latest/library/machine.html http://docs.micropython.org/en/latest/library/micropython. html http://docs.micropython.org/en/latest/library/neopixel.html http://docs.micropython.org/en/latest/library/network.html http://docs.micropython.org/en/latest/library/uctypes.html СОВЕТ Дополнительные сведения о любой из библиотек MicroPython см. на странице http://docs.micropython.org/en/latest/library/index.html#micropython-specific-libraries. Далее мы рассмотрим несколько наиболее распространенных библиотек MicroPython и примеры кода для каждой. Общие библиотеки MicroPython Теперь давайте посмотрим на примеры некоторых наиболее часто используемых библиотек MicroPython. Далее следует лишь пример того, что вы можете сделать с каждой из библиотек. Полное описание всех возможностей смотрите в онлайн-документации. Библиотека machine Библиотека machine содержит функции, связанные с аппаратным обеспечением, обеспечивая уровень абстракции, на котором вы можете писать код для взаимодействия с аппаратным обеспечением. Таким образом, эта библиотека является основной библиотекой, которую вы будете использовать для доступа к таким функциям, как таймеры, протоколы связи, процессор и многое другое. Поскольку эта функция напрямую взаимодействует с оборудованием, вам следует проявлять осторожность при экспериментировании, чтобы избе-
192  Библиотеки MicroPython жать изменения или даже потенциального повреждения функциональности либо конфигурации вашей платы. Неправильное использование библиотеки может, например, привести к зависаниям, перезагрузкам или сбоям. ВНИМАНИЕ! Будьте осторожны при работе с библиотекой machine низкого уровня, чтобы не изменить или даже потенциально не повредить функциональность либо конфигурацию вашей платы. Поскольку библиотека machine представляет собой аппаратную абстракцию низкого уровня, мы не будем рассматривать ее подробно в этой главе. Скорее, в следующей главе мы увидим больше аппаратных функций. А пока давайте рассмотрим еще одну интересную жемчужину знаний MicroPython, показав вам, как узнать, что содержит библиотека, с помощью функции справки. Например, в лис­тин­ге 5.8 показан фрагмент того, что сообщается через консоль REPL, когда мы запускаем оператор help(machine). Функция help() отобразит все функции и константы, доступные для использования в библиотеке. Хотя эта функция не заменяет подробного объяснения или даже полного примера, она может быть полезна при первом знакомстве с библиотекой. Листинг 5.8  Справка по библиотеке machine >>> import machine >>> help(machine) object <module 'umachine'> is of type module __name__ -- umachine unique_id -- <function> soft_reset -- <function> reset -- <function> reset_cause -- <function> bootloader -- <function> freq -- <function> idle -- <function> lightsleep -- <function> deepsleep -- <function> disable_irq -- <function> enable_irq -- <function> bitstream -- <function> time_pulse_us -- <function> dht_readinto -- <function> mem8 -- <8-bit memory> mem16 -- <16-bit memory> mem32 -- <32-bit memory> ADC -- <class 'ADC'> I2C -- <class 'I2C'> SoftI2C -- <class 'SoftI2C'> I2S -- <class 'I2S'> Pin -- <class 'Pin'> PWM -- <class 'PWM'> RTC -- <class 'RTC'> Signal -- <class 'Signal'> SPI -- <class 'SPI'>
Библиотеки MicroPython  193 SoftSPI -- <class 'SoftSPI'> Timer -- <class 'Timer'> UART -- <class 'UART'> WDT -- <class 'WDT'> PWRON_RESET -- 1 WDT_RESET -- 3 Обратите внимание, здесь очень много информации! Больше всего это дает нам список классов, которые мы можем использовать для взаимодействия с оборудованием. Здесь мы видим классы для UART, SPI, I2C, PWM и других аппаратных функций. Поскольку платы MicroPython от разных производителей могут содержать различное оборудование, всегда полезно проверить результат команды help(machine) на плате, которую вы используете впервые. Это может избавить вас от головной боли при поиске поддержки несуществующего оборудования! Библиотека network Сетевая библиотека network используется для установки сетевых драйверов (классов взаимодействия с сетевой абстракцией) и настройки параметров. Однако важно отметить, что эту библиотеку будут иметь только платы с сетевыми возможностями. Например, Raspberry Pi Pico (оригинал без Wi-Fi) выдает ошибку при попытке импортировать библиотеку следующим образом: >>> import network Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: no module named 'network' Однако Raspberry Pi Pico W поддерживает библиотеку следующим образом: >>> import network >>> help(network) object <module 'network'> is of type module __name__ -- network country -- <function> hostname -- <function> route -- <function> WLAN -- <class 'CYW43'> STAT_IDLE -- 0 STAT_CONNECTING -- 1 STAT_WRONG_PASSWORD -- -3 STAT_NO_AP_FOUND -- -2 STAT_CONNECT_FAIL -- -1 STAT_GOT_IP -- 3 STA_IF -- 0 AP_IF -- 1 Хотя и другие библиотеки различаются от одной платы к другой, эта биб­ лиотека сильно отличается для разных плат MicroPython. В следующей главе мы узнаем больше о сетевых классах и интересном классе Bluetooth.
194  Библиотеки MicroPython Пользовательские библиотеки Создание собственных библиотек может показаться сложной задачей, но это не так. Что, возможно, является некоторой проблемой, так это выяснить, что вы хотите от библиотеки, и сделать ее достаточно абстрактной, чтобы ее можно было использовать в любом сценарии. Здесь вступают в игру правила и лучшие практики программирования, такие как абстракция данных, неизменяемость API и т. д. В предыдущей главе мы обсуждали создание наших собственных модулей. В этом разделе мы рассмотрим, как организовать наши модули кода в биб­ лиотеку (пакет), которую мы можем развернуть (скопировать) на нашу плату MicroPython и использовать во всех наших программах. Этот пример, хотя и тривиальный, представляет собой полный пример, который вы можете использовать в качестве шаблона, если решите создать свои собственные библиотеки. В этом примере мы создадим библиотеку с двумя модулями: один содержит код для выполнения преобразования значений для датчика, а другой содержит вспомогательные функции для наших проектов – общие функции, которые мы хотим использовать повторно. Мы назовем библиотеку my_helper. Она будет содержать два модуля кода: sensor_convert.py и helper_functions.py. Напомним, нам также понадобится файл __init__.py, чтобы помочь MicroPython правильно импортировать функции, но мы вернемся к этому чуть позже. Давайте посмотрим на первый модуль кода. Мы поместим файлы в каталог с именем my_helper (тот же, что и имя биб­ лиотеки). Это типичное соглашение, и вы можете указать любое имя, которое захотите, но вы должны его запомнить, поскольку мы будем использовать это имя при импорте библиотеки в наш код. На рис. 5.1 показан пример расположения файлов. Теперь давайте посмотрим на код. Первый модуль называется helper_functions.py и содержит одну из вспомогательных функций из предыдущего примера. Поскольку они являются общими, мы поместили их в этот модуль кода, при этом мы хотим использовать этот код на всех наших платах. В лис­тин­ ге 5.9 показан полный код модуля. Листинг 5.9  Модуль helper_functions.py # MicroPython for the IoT Second Edition - Chapter 5 # Example module for the my_helper library # This module contains helper functions for general use. import machine # Format the time (epoch) for a better view def format_time(tm_data): # Use a special shortcut to unpack tuple: *tm_data return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format( *tm_data)
Пользовательские библиотеки  195 Рис. 5.1  Структура каталога/файла библиотеки примеров my_helper на Raspberry Pi Pico W Второй модуль кода называется sensor_convert.py и содержит функции, которые помогают преобразовать необработанные значения датчика в строку для качественного сравнения. Например, функция get_moisture_level() возвращает строку влагосодержания почвы на основе порогового значения необработанного значения. В таблице данных датчика будут определены такие значения, и вам следует использовать их в своем коде до тех пор, пока вы не сможете откалибровать датчик. При этом если значение меньше нижней границы, почва сухая; если больше верхней границы, почва влажная. В лис­ тин­ге 5.10 показан полный код модуля. Листинг 5.10  Модуль sensor_convert.py # MicroPython for the IoT Second Edition - Chapter 5 # Example module for the my_helper library # This function converts values read from the sensor to a # string for use in qualifying the moisture level read. # Constants - adjust to "tune" your sensor _UPPER_BOUND = 400 _LOWER_BOUND = 250 def get_moisture_level(raw_value): if raw_value <= _LOWER_BOUND: return("dry")
196  Библиотеки MicroPython elif raw_value >= _UPPER_BOUND: return("wet") return("ok") Теперь давайте рассмотрим файл __init__.py. Это очень загадочный файл, который часто приводит в замешательство разработчиков. Если вы не включили его в каталог своей библиотеки, вам следует импортировать то, что вы хотите использовать, вручную, то есть с помощью чего-то вроде import. my_helper.helper_functions. Но с помощью файла вы можете выполнить импорт одновременно, используя простой оператор import my_helper, который импортирует все файлы. Давайте посмотрим на файл __init__.py. В лис­тин­ ге 5.11 показано содержимое файла. Листинг 5.11  Файл __init__.py # MicroPython for the IoT Second Edition - Chapter 5 # Metadata __name__ = "Chuck's Python Helper Library" __all__ = ['format_time', 'get_rand', 'get_moisture_level'] # Library-level imports from my_helper.helper_functions import format_time from my_helper.sensor_convert import get_moisture_level Обратите внимание, что в первой строке мы используем специальную константу для установки имени библиотеки. Следующая константа ограничивает то, что будет импортировано с помощью опции * (all) для оператора импорта. Поскольку в нем перечислены все методы, это всего лишь упражнение, но хорошая привычка, особенно если ваша библиотека и модули содержат много внутренних функций, которые вы не хотите делать доступными другим. Последние две строки показывают операторы импорта, используемые для импорта функций из модулей, что делает их доступными для всех, кто импортирует библиотеку. Ниже показан краткий пример того, как это сделать, а также как использовать псевдоним. Здесь мы используем myh в качестве псевдонима для my_helper: >>> import my_helper as myh >>> import time >>> myh.format_time(time.localtime()) '2023-07-16 16:08:26' Теперь мы можем получить доступ ко всем функциям (использовать их) по этому псевдониму следующим образом: >>> import my_helper as myh >>> import time >>> myh.format_time(time.localtime()) '2023-07-16 16:12:27' >>> print(myh.get_moisture_level(750)) Wet
Итоги  197 Если вам интересно, функция справки работает и в этой пользовательской библиотеке! >>> import my_helper as myh >>> help(myh) object <module 'Chuck's Python Helper Library' from 'my_helper/__init__.py'> is of type module __path__ -- my_helper get_moisture_level -- <function get_moisture_level at 0x20012f70> __name__ -- Chuck's Python Helper Library __file__ -- my_helper/__init__.py format_time -- <function format_time at 0x20012b00> helper_functions -- <module 'my_helper.helper_functions' from 'my_helper/helper_functions. py'> __all__ -- ['format_time', 'get_rand', 'get_moisture_level'] sensor_convert -- <module 'my_helper.sensor_convert' from 'my_helper/sensor_convert.py'> После того как вы начали экспериментировать с MicroPython и завершили несколько проектов, вы можете начать создавать набор функций, которые будете использовать время от времени. Это идеальные кандидаты для размещения в библиотеке. Совершенно нормально, если функции не являются частью более крупного класса или объекта. Если вы организуете их в модули с одинаковой функциональностью, вам, возможно, не придется беспокоиться о том, чтобы превратить их в классы. С другой стороны, если задействованы данные или набор функций работает с набором данных, вам следует подумать о том, чтобы превратить этот набор функций в класс для более удобного использования и повышения качества кода. Подождите, а что насчет CircuitPython? Напомним, в главе 3 мы обсуждали CircuitPython, когда рассматривали плату Circuit Playground от Adafruit. В этой главе не рассматривается более подробно CircuitPython, поскольку это порт MicroPython, и поэтому применимо все, что мы знаем о библиотеках MicroPython. Отличие заключается в некоторых библиотеках и функциях, специфичных для плат: в CircuitPython есть несколько расширенных библиотек, разработанных для плат Adafruit. Для получения дополнительной информации о CircuitPython см. https://circuitpython.readthedocs.io/en/stable/. Итоги Прошивка MicroPython обладает множеством возможностей для IoT-про­ ек­тов. Действительно, существует много различных классов, которые мы можем использовать для написания надежных и сложных программ MicroPython с применением встроенных функций, которые дают языку широкие
198  Библиотеки MicroPython возможности для работы с данными, выполнения вычислений и даже работы со значениями времени, взаимодействуя с аппаратным обеспечением напрямую. Работа с аппаратным обеспечением – это одна из областей, которая больше всего отличается в MicroPython от одной платы к другой. Платы совершенно разные: где-то есть сеть, где-то нет. Некоторые имеют больше встроенных функций, чем другие, а некоторые имеют меньше памяти и даже меньше контактов GPIO. Неудивительно, что прошивка разных плат отличается, когда дело касается уровней аппаратной абстракции. В этой главе мы рассмотрели некоторые из наиболее часто используемых встроенных библиотек и библиотек MicroPython, которые обычно применяются ко всем платам. В следующей главе мы углубимся в библиотеки нижнего уровня и аппаратную поддержку в MicroPython.
Глава 6 Низкоуровневая поддержка оборудования По самой базовой функциональности прошивки MicroPython одинаковы от платы к плате для всех поддерживаемых общих языков Python и многих встроенных функций. Однако некоторые библиотеки прошивки MicroPython имеют несколько незначительных отличий от одной платы к другой. В некоторых случаях доступно больше библиотек или классов, чем в других, или, возможно, классы организованы по-разному, но большинство из них реализуют одни и те же основные библиотеки в той или иной форме. Нельзя сказать, что то же самое справедливо и для нижних уровней аппаратной абстракции. Это просто потому, что один поставщик плат может использовать другое оборудование, чем другие. В некоторых случаях плата имеет функции, которых нет на других платах. В этой главе мы рассмотрим несколько примеров низкоуровневой аппаратной поддержки в MicroPython. Мы узнаем о библиотеках для конкретных плат, а также о некоторых специализированных библиотеках более низкого уровня, например для Bluetooth, SPI, I2C и других. Мы также увидим примеры кода, иллюстрирующие возможности библиотек для конкретных плат. Некоторые из них будут представлять собой короткие фрагменты кода, а не реальные проекты, которые вы можете реализовать самостоятельно. Тем не менее есть несколько законченных проектов, которые вы, возможно, захотите изучить, но большинство из них объяснены без особой глубины и предназначены для примеров, а не для детального ознакомления. Кроме того, имейте в виду, что для реализации им, скорее всего, потребуются специальный модуль и плата MicroPython, а также другие аксессуары. Опять же, это для демонстрационных целей. В последующих главах мы увидим более полные пошаговые примеры, включая сборку оборудования. Для краткости мы рассмотрим библиотеки, специфичные для RP2040, и некоторые функции, которые различаются на платах Pico и Arduino. Другие платы могут отличаться, но вам нужно будет проверить документацию по-
200  Низкоуровневая поддержка оборудования ставщика, чтобы узнать, чем они различаются. Данная глава должна дать вам представление о том, как найти эти различия. Мы также вернемся к работе с внешними модулями, чтобы продемонстрировать некоторые библиотеки, аппаратные протоколы и методы, обсуждавшиеся в предыдущих главах. Возможно, при первой работе с новой платой MicroPython вам следует помнить одну вещь: прошивка на аппаратном уровне, скорее всего, будет отличаться от прошивки последней платы MicroPython, которую вы использовали. Это особенно актуально, если вы посмотрите на порты прошивки для таких плат, как BBC micro:bit, Circuit Playground, ESP8266 и т. д. СОВЕТ Вы можете увидеть различия в поддержке библиотек низкого уровня для других плат по адресу https://docs.micropython.org/en/latest/library/index.html, кликнув по ссылкам на перечисленные платы, такие как Pyboard или ESP8266. Давайте начнем с рассмотрения библиотек, специфичных для RP2040, для плат на базе Pico и Arduino RP2040. Библиотеки, специфичные для RP2040 Библиотеки MicroPython, специфичные для RP2040, содержатся в модуле с именем rp2. Эта библиотека включена в вашу установку MicroPython для любой платы, использующей набор микросхем RP2040. Таким образом, для использования библиотеки вам не нужно загружать какой-либо дополнительный код. В модуле rp2 доступны три класса, включая следующие:   Flash: операции со встроенной флеш-памятью;   StateMachine: базовый класс для работы с программируемым вводом/ выводом;   PIO: расширенные программируемые операции ввода/вывода. Самый интересный класс для новичков в MicroPython – это класс Flash. Два других класса полезны для создания расширенных кодовых решений, использующих преимущества функциональности программируемого ввода-вывода (PIO) RP2040. Мы кратко обсудим каждую из этих особенностей в следующих разделах. Класс Flash Класс Flash предоставляет возможность работы напрямую с файловой системой флеш-памяти. Это может быть удобно, если вы хотите использовать низкоуровневое хранилище данных, но в целом вам рекомендуется использовать существующие библиотеки MicroPython более высокого уровня для чтения и записи файлов.
Библиотеки для конкретных плат  201 Классы PIO и StateMachine Классы PIO и StateMachine предоставляют возможность добавлять дополнительные интерфейсы или протоколы, например дополнительную поддержку связи по последовательному порту. Это вряд ли потребуется большинству проектов Pico, но необходимо, если вы обнаружите, что вам нужен еще один интерфейс сверх предоставленного аппаратным обеспечением Pico. С помощью этих классов вы можете создавать свои собственные низкоуровневые механизмы доступа к оборудованию. Например, если у вас есть датчик, которому требуется считывать данные быстрее, чем это поддерживается существующими аппаратными и программными библиотеками, или если устройству требуется определенная последовательность команд или откликов, вы можете привлечь эти классы, чтобы использовать программное обеспечение для формирования интерфейса аппаратуры. Такие сегменты кода загружаются и выполняются в специальном ядре обработки, позволяющем запускать до восьми процессов. Полное руководство по использованию PIO можно найти в главе 3 книги технического описания RP2040 (https:// datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf). Если вы хотите более подробно изучить эти классы или узнать больше о поддержке PIO, вы можете найти пример кода по адресу https://github.com/ raspberrypi/pico-micropython-examples/tree/master/pio. Далее давайте посмотрим на библиотеки для конкретных рекомендуемых плат MicroPython. Библиотеки для конкретных плат Каждая плата MicroPython может иметь одну или несколько функций (компонентов), которые не включены в базовые библиотеки MicroPython. Например, если на вашей плате есть Wi-Fi или дополнительные датчики, базовые библиотеки MicroPython могут содержать поддержку работы с этими функциями. К счастью, большинство поставщиков предоставляют дополнительные библиотеки, которые вы можете использовать, отсюда и название boardspecific libraries – «библиотеки для конкретных плат». Эти библиотеки могут быть включены в специальный загрузочный образ MicroPython для конкретной платы или могут быть доступны для загрузки и установки. Поскольку обе наши рекомендуемые платы (Raspberry Pi Pico и Arduino Nano RP2040 Connect) используют набор микросхем RP2040, в базовых библиотеках есть все необходимое. При этом Arduino Nano RP2040 Connect имеет дополнительные функции, требующие дополнительных биб­ лиотек. СОВЕТ Если вы используете другую плату MicroPython, посетите веб-сайт поставщика для получения дополнительной информации о библиотеках или загрузочных образах для конкретной платы, которые вам, возможно, потребуется загрузить и установить.
202  Низкоуровневая поддержка оборудования Библиотеки, специфичные для Raspberry Pi Pico Raspberry Pi Pico имеет одну особенность платы – модуль Wi-Fi, который немного отличается от Arduino Nano RP2040 Connect. Чтобы использовать Wi-Fi на Pico, мы импортируем сетевой модуль, а затем используем класс WLAN и вызываем метод connect(), передавая имя Wi-Fi (SSID) и пароль. Затем мы можем зациклить ожидание соединения, вызвав методы isconnected() и status(), как показано в лис­тин­ге 6.1. Он показывает, насколько легко подключиться к Wi-Fi на Pico. Более подробную информацию о работе с Wi-Fi мы увидим в последующих примерах. Листинг 6.1  Подключение к Wi-Fi (Pico) # # MicroPython for the IoT Second Edition - Chapter 6 # # Example use of the network library # WiFi for Raspberry Pi Pico # # Dr. Charles Bell # import network, time wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect('SSID', 'password') while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect to WiFi:") time.sleep(1) print("Success!\nMy IP address is: {0}".format(wlan.ifconfig()[0])) Библиотеки, специфичные для Arduino Nano RP2040 Connect Хотя разводка выводов GPIO для Arduino Nano RP2040 Connect отличается от Raspberry Pi Pico, большая часть функций одинакова, поскольку они оба используют RP2040. По этой причине примеры проектов в этой книге будут содержать схемы подключения для обеих плат с обозначениями для кода, специфичного для конкретной платы. Однако Arduino Nano RP2040 Connect предоставляет дополнительные функции, внедренные в специальный интерфейс прикладного программирования (API), включенный в последнюю версию загрузочного образа для платы. Таким образом, как и в случае с Pico, вам не нужно устанавливать какие-либо специальные библиотеки, чтобы использовать функции, специ­ фичные для платы.
Работа с оборудованием на низком уровне  203 Arduino Nano RP2040 оснащен двумя датчиками, другим модулем Wi-Fi и энергосберегающим интерфейсом Bluetooth (BLE). В табл. 6.1 перечислены функции, специфичные для платы, а также пример кода, показывающий, как использовать плату в ваших сценариях. В примерах далее мы увидим, как использовать некоторые из этих функций. Таблица 6.1. Arduino Nano RP2040 Connect – специфические особенности и библиотеки Категория Датчик Датчик Интерфейс Устройство IMU (LSM6DSOX) Описание Акселерометр и гироскоп Микрофон (MP34DT 06JTR) Аудиомикрофон I2C Связь по шине I2C Беспроводной WiFi интерфейс Беспроводной Bluetooth Low Energy интерфейс Беспроводная связь Образец кода from lsm6dsox import LSM6DSOX import audio from machine import Pin, I2C import network Связь через BLE import bluetooth Arduino Nano RP2040 Connect также можно запрограммировать с использованием усовершенствованной интегрированной среды разработки (IDE) машинного зрения под названием OpenMV. Эта IDE предоставляет альтернативу (называемую форком) MicroPython, включающую функции визуализации. Если вы хотите узнать больше об OpenMV или изучить какую-либо из этих функций самостоятельно, на веб-сайте Arduino содержится пример кода для использования каждой из этих функций. См. https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-pythonapi для получения последней документации по библиотекам Arduino Nano RP2040 Connect. Теперь, когда мы кратко познакомились с библиотеками, специфичными для RP2040 и плат, давайте обсудим, как работают низкоуровневые библиотеки и зачем они нам нужны. Работа с оборудованием на низком уровне Работа с аппаратным обеспечением на низком уровне – это то, где происходят все действия и куда, по сути, обращено основное внимание (и вся относительная сложность) использования MicroPython. MicroPython и производители коммутационных плат проделали отличную работу, упрощая нам задачу, но объяснения еще можно улучшить. То есть документация в интернете несколько ужата, когда речь идет о низкоуровневых примерах использования оборудования. Частично это связано
204  Низкоуровневая поддержка оборудования с тем, что примеры нередко требуют дополнительного специального оборудования и программного обеспечения. Например, для работы с интерфейсом I2C вам понадобится модуль с поддержкой I2C, а также программная библиотека для «общения» с такой платой. Таким образом, онлайн-примеры содержат только самые простые примеры и объяснения. За исключением встроенных датчиков, большая часть низкоуровневой связи будет осуществляться через интерфейсы I2C, 1-Wire, аналоговые или цифровые контакты или иногда интерфейсы SPI. Интерфейсы I2C и SPI – это те интерфейсы, с которыми вы, вероятно, столкнетесь с наибольшими трудностями при работе с оборудованием. Это связано с тем, что для каждого используемого вами устройства (подключаемой платы, модуля) на низком уровне потребуется очень специфический протокол. То есть устройству может потребоваться особая последовательность срабатывания датчика или функции устройства, отличающаяся от других подключаемых модулей. Таким образом, работа с устройствами типа I2C или SPI (и некоторыми другими) может стать проблемой в плане точного знания, как с ними «общаться». Драйверы и библиотеки спешат на помощь! К счастью, существует небольшое, но растущее число людей, создающих классы и наборы функций, которые помогут нам работать с этими устройствами. Они называются библиотеками или иногда драйверами и представляют собой один или несколько модулей кода, которые вы можете загрузить, скопировать на свою плату и импортировать функциональные возможности в свою программу. Разработчики драйверов выполнили за вас всю тяжелую работу, сделав использование устройств как можно проще. Таким образом, большинству людей, которые только начинают работать с MicroPython и хотят работать с определенными датчиками, устройствами, внешними модулями и т. д., следует ограничить то, что вы планируете использовать, устройствами, для которых вы можете найти драйвер. Итак, как найти драйвер для вашего устройства? Есть несколько мест, где стоит посмотреть. Прежде всего вам следует просмотреть форумы и документацию по MicroPython. В этом случае не ограничивайте себя только теми форумами, которые подходят вашему выбору точно. Скорее всего, вы сможете найти библиотеку, которую сможете адаптировать лишь с небольшими изменениями. Большинство из них можно использовать, практически не прилагая никаких усилий, кроме загрузки и копирования на плату. Ниже перечислены основные форумы и документация, которые следует часто посещать при поиске драйверов:     форумы MicroPython: https://forum.micropython.org/; документация MicroPython: https://docs.micropython.org/en/latest/; обучение Adafruit: https://learn.adafruit.com/; документация Pico: www.raspberrypi.org/documentation/rp2040/gettingstarted/.
Работа с оборудованием на низком уровне  205 Есть также ряд документов, которые вы можете скачать и прочитать в автономном режиме. Ниже приведены некоторые из наиболее важных документов Pico:   техническое описание Pico: https://datasheets.raspberrypi.org/pico/picodatasheet.pdf;   техническое описание RP2040: https://datasheets.raspberrypi.org/rp2040/ rp2040-datasheet.pdf;   руководство по проектированию оборудования: https://datasheets.raspberrypi.org/rp2040/hardware-design-with-rp2040.pdf;   руководство Pico MicroPython: https://datasheets.raspberrypi.org/pico/ raspberry-pi-pico-python-sdk.pdf. Во-вторых, используйте свою любимую поисковую систему в интернете и найдите примеры применения оборудования. Используйте в поиске имя аппаратного устройства и «MicroPython». Если устройство новое, вы можете не найти точных совпадений по критериям поиска. Обязательно изучите и другие условия поиска. Как только вы найдете драйвер, начнется самое интересное! Вам следует скачать и скопировать его на плату для тестирования. Обязательно следуйте примеру, прилагаемому к драйверу, чтобы избежать непредвиденных проблем. Это напоминает об одной важной вещи, которую следует учитывать при принятии решения о том, хотите ли вы использовать драйвер. Если драйвер хорошо документирован и имеет примеры (особенно если пример написан для Pico), вы можете чувствовать себя в безопасности, используя его. Однако если драйвер вообще не документирован, или образец кода отсутствует либо непонятен, или он написан для другой платы, возможно, вы не захотите его использовать. Есть большая вероятность, что он недоделанный, старый, в стадии разработки или просто плохо закодирован. Не все, кто делится, могут делать хорошие продукты. При работе с примерами проектов в этой книге мы увидим несколько примеров библиотек. Как вы увидите, не все из них так просто загрузить и использовать. Давайте рассмотрим два низкоуровневых примера: работу с установкой времени из интернета с использованием протокола сетевого времени (NTP) и обратные вызовы через прерывания. Начнем с примера NTP. Это лишь малая часть того, что доступно, и представляет собой наиболее распространенные вещи, с которыми вам придется работать в этой книге, а также в большинстве небольших проектов интернета вещей. Сетевой протокол времени (NTP) Некоторые платы MicroPython и некоторые платы расширения имеют часы реального времени (RTC). RTC – это специальная схема (интегральная схема или чип), которая отсчитывает время. Это связано с тем, что большинство процессоров (микроконтроллер, микропроцессор и т. д.) работают синхронно
206  Низкоуровневая поддержка оборудования с кристаллом или тактовым чипом, который поддерживает работу процессора на определенной частоте (измеряемой обычно в МГц). К сожалению, эту частоту часто нелегко перевести в переменные значений времени. RTC используется для хранения значений времени, чтобы мы могли применять время для записи событий. Чтобы использовать RTC, мы сначала инициализируем начальное значение текущей датой и временем (так же, как, например, устанавливаем новые настольные часы) и можем прочитать текущую дату и время, когда захотим. Однако RTC без резервной батареи потеряет свои значения при выключении платы. Таким образом, мы должны устанавливать такие RTC каждый раз, когда запускаем плату. К счастью, в интернете есть служба времени, с помощью которой мы можем узнать текущую дату и время. Она называется сетевым протоколом времени (network time protocol, NTP). ПРИМЕЧАНИЕ Этот пример предназначен для демонстрации использования встроенных библиотек, таких как библиотека rtc. Мы рассмотрим код более подробно в следующем примере проекта. Однако следует отметить, что для плат расширения с модулями RTC может потребоваться собственная библиотека, предоставляемая поставщиком платы, которая заменяет использование библиотеки rtc. Давайте посмотрим на примере, как использовать этот протокол. Мы создадим на Pico программу, которая подключит плату к нашему локальному Wi-Fi с доступом в интернет1. После подключения мы будем использовать NTP для установки текущего времени, а затем выполним тест, чтобы узнать текущую дату и время. Мы должны увидеть точную дату и время запуска кода! В лис­тин­ге 6.2 показан законченный пример. ПРИМЕЧАНИЕ Raspberry Pi Pico не имеет резервной батареи, и хотя вы можете установить дату и время, значения будут потеряны при перезагрузке. Листинг 6.2  Использование сервера времени NTP для установки RTC (Pico) # # MicroPython for the IoT Second Edition - Chapter 6 # # Example use of the ntptime library and connecting to an NTP service # # Dr. Charles Bell # import network, ntptime, time, socket, struct from machine import RTC NTP_HOST = "us.pool.ntp.org" TZ_DELTA = 4 * 3600 # num_hours * num_mins_in_hour NUM_RETRIES = 3 # number of retries for ntp server call # Return time now adjusted for timezone def now(): return time.localtime(time.time() - TZ_DELTA) 1 Разумеется, автор имел в виду Pico W. – Прим. перев.
Работа с оборудованием на низком уровне  207 # Connect to WiFi def connect_wifi(): print("Connecting to WiFi... ", end="") wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect('SSID', 'password') while not wlan.isconnected() and wlan.status() >= 0: print("retrying... ", end="") time.sleep(1) print("done.") print("Network values: {0}".format(wlan.ifconfig())) # Connect to NTP server and set localtime def set_ntp_time(): print("Contacting NTP Server... ", end=("")) for i in range(0, NUM_RETRIES): try: ntptime.settime() except: print("retrying... ", end=("")) time.sleep(3) print("done.") break connect_wifi() # Begin NPT synchronization print("Time Before NPT: {0}".format(time.localtime())) set_ntp_time() print("Time after NPT : {0}".format(now())) Большая часть этого кода должна быть вам знакома, поскольку в предыдущем разделе мы осуществляли подключение Wi-Fi. В этом примере мы помещаем код в метод, чтобы его было немного проще использовать. Однако применение класса ntptime является новым. Обратите внимание, что все, что нам нужно сделать после установления сетевого подключения, – это вызвать ntp_settime(). Да, он встроен в библиотеку! После этого нужно только настроить местный часовой пояс, и все готово. Посмотрите метод now(), как это делается. Также обратите внимание, что код написан с использованием методов для каждого из основных шагов (connect_wifi(), set_ntp_time() и now()). Обратите внимание, что set_ntp_time() был написан с использованием цикла, который помогает с тайм-аутами соединения. Если ваша сеть работает медленно, возможно, вам придется увеличить количество повторов NUM_RETRIES. Когда вы запустите это на Pico, то увидите результат, показанный ниже. Обратите внимание, что перед вызовом NTP выводится на экран значение местного времени (time.localtime), а затем снова выводим местное время, как только оно синхронизируется с NTP: Connecting to WiFi... retrying... done. Network values: ('192.168.1.205', '255.255.255.0', '192.168.1.1', '192.168.1.1') Time Before NPT: (2023, 8, 26, 21, 19, 5, 5, 238)
208  Низкоуровневая поддержка оборудования Contacting NTP Server... retrying... done. Time after NPT : (2023, 8, 26, 17, 19, 15, 5, 238) Этот пример может быть очень полезным, а в некоторых случаях и обязательным при чтении данных для последующего анализа. Часто бывает важно знать, когда данные были сохранены или когда датчик был прочитан. Возможно, вы захотите зарезервировать этот код для дальнейшего использования в своих IoT-проектах. ПРИМЕЧАНИЕ Мы можем использовать специальный модуль RTC со встроенными часами, которые поддерживают синхронизацию часов при работе в автономном режиме или в периоды, когда питание отключено. Как это сделать, мы увидим в главе 8. Теперь давайте посмотрим на обратные вызовы – прием программирования, который можно использовать для работы с аппаратными прерываниями и таймерами. Обратные вызовы Что делать, если вы хотите, чтобы какой-то фрагмент кода выполнялся в ответ на датчик или ввод пользователя? Используя то, что мы узнали до сих пор, мы могли бы написать нашу программу с циклом для опроса датчика или устройства, реагирующего на действия пользователя (например, кнопки), и при срабатывании выполнить код. Этот метод опроса будет работать, но есть конструкция получше, называемая обратным вызовом. Обратные вызовы – это функции, которые мы определяем и связываем с прошивкой для выполнения при возникновении определенного события. Если аппаратная абстракция позволяет использовать обратный вызов, мы можем это использовать. К счастью, класс Timer имеет такой механизм. Аналогичным образом мы могли бы использовать и аппаратные прерывания. Однако аппаратные прерывания – это более сложная тема1. Давайте поработаем с классом Timer, чтобы упростить задачу. 1 Вообще говоря, обратным вызовом (callback) называют передачу исполняемого ко­да в качестве одного из параметров другому коду (в аргументах при его вызове). Случай обработки события с помощью callback-функции – лишь один из частных случаев ее применения. Так, в примере автора ниже функции timer.init передается код функции toggle_led. В современных языках программирования (PHP, JavaScript, Perl, Python, Ruby и пр.) callback-функции широко используются, однако следует учитывать, что это чисто программистский прием, который только ухудшает читаемость текста программы и замедляет ее выполнение. С точки зрения электронщика обычные аппаратные прерывания по вектору нагляднее, не имеют ограничений на обмен данными (см. предостережения ниже) и к тому же выполняются быстрее. Подобная функциональность, характерная для классических AVR, есть и в архитектуре RP2040, см. ее полное описание. Однако «стиль Python» предписывает аппаратные прерывания также привязывать к callback-функциям (см. текст автора далее, а также раздел документации по MicroPython: https://docs.micropython.org/en/ latest/pyboard/tutorial/switch.html). – Прим. перев.
Работа с оборудованием на низком уровне  209 Использование обратных вызовов позволяет продолжать выполнение кода для выполнения таких работ, как считывание датчиков, отображение данных и т. д., а при возникновении события (прерывания) MicroPython выполнит функцию обратного вызова, а затем вернется к выполнению основного кода. Это работает путем привязки функции обратного вызова к прерываниям. В классе Timer определено прерывание с вызовом кода для выполнения работы различными способами. Мы будем использовать периодическую опцию, которая будет вызывать обратный вызов и указывать интервалы, что идеально подходит для мигания светодиодами, проверки датчиков и т. д. Есть несколько предостережений относительно использования обратного вызова Timer. Во-первых, функция обратного вызова не может принимать параметры, поэтому вы не можете определить функцию обратного вызова и передать ей какие-либо данные. Фактически функциям обратного вызова не разрешено создавать данные, это только указание действий. Например, вы не можете создать словарь, кортеж и т. д. внутри функции. Хотя функции обратного вызова могут обращаться к глобальным переменным, они могут генерировать исключения, если вы используете переменные состояния. Наконец, вы можете выключить (отключить) обратный вызов, например, с помощью timer.deinit(). Теперь давайте посмотрим пример. В этом примере мы хотим создать обратный вызов для переключения светодиода на плате. Настроить обратный вызов совсем несложно. Мы просто указываем функцию обратного вызова для таймера и передаем имя функции. Давайте посмотрим код, и вы поймете, как это работает. В лис­тин­ге 6.3 показан готовый код примера обратного вызова для Pico. Листинг 6.3  Пример обратного вызова (Pico) # # MicroPython for the IoT Second Edition - Chapter 6 # # Example use of callbacks with the Timer class to blink an LED # # Dr. Charles Bell # from machine import Pin, Timer led = Pin("LED", Pin.OUT) # Get the onboard LED as output led.off() # Make sure it's off first # Callback for toggling the LED on/off def toggle_led(timer): global led # fetch the global led variable led.toggle() # Get instance of Timer class timer = Timer() # Initialize the timer to periodically call the callback method # once every second timer.init(freq=1, mode=Timer.PERIODIC, callback=toggle_led)
210  Низкоуровневая поддержка оборудования Теперь давайте посмотрим, как взаимодействовать с внешними модулями с использованием протоколов I2C и SPI. Использование внешних модулей Внешние модули1 – один из ключевых элементов, которые любители и энтузиасты используют при создании IoT-решений на MicroPython (или любом другом языке). Это связано с тем, что внешние модули представляют собой небольшие печатные платы, которые содержат все компоненты, необходимые для поддержки таких функций, как датчик, сетевой интерфейс или даже дисплей. Такие платы также поддерживают один из протоколов связи, для которых требуется подключение всего нескольких контактов, что делает их очень простыми в использовании. В общем, они экономят разработчику много времени, теряемого в попытках понять, как спроектировать схемы для поддержки датчика или чипа. Существует два метода работы с внешними модулями: поиск драйвера, который вы можете использовать, или создание собственного драйвера. Создание собственного драйвера не рекомендуется для новичков в MicroPython. Гораздо проще потратить время на поиск драйвера, который можно использовать (или адаптировать), чем пытаться написать его самостоятельно. Это связано с тем, что вы должны уметь получать данные, читать и понимать, как функционирует данная плата (понять протокол ее обмена). Каждая плата будет обмениваться данными по-разному, в зависимости от поддерживаемого датчика или устройств. То есть драйвер для датчика BMP180 не обязательно будет работать так же, как драйвер для датчика BME280. Вы должны быть очень конкретны при поиске и использовании драйвера. Поиск драйвера может оказаться утомительным занятием, требующим некоторого терпения и, возможно, долгих поисков на форумах с использованием различных поисковых запросов, таких как «micropython BME280». Найдя драйвер, вы сможете быстро определить, является ли он жизнеспособным вариантом, просмотрев приложенный пример. Как упоминалось ранее, если примера нет или он не похож ни на что, что вы видели в этой книге или в онлайн-документации, не используйте его. Давайте рассмотрим два примера внешних модулей: один использует протокол I2C, другой – протокол SPI. Мы будем следовать шаблону объяснения примеров, который используется на протяжении всей книги, чтобы представить проект и необходимые компоненты, показать, как настроить оборудование (соединить все вместе), написать код и, наконец, выполнить его. 1 В англоязычных странах принят термин breakout boards (досл. «разделительная плата»), в русском языке подобное общее название отсутствует; соответствует оте­ чественным терминам «внешний модуль», «плата (модуль) расширения», «модуль датчиков», «интерфейсный модуль» и т. д. – Прим. перев.
Использование внешних модулей  211 Ценность онлайн-примеров Если вы хотите использовать внешний модуль в своем IoT-проекте, обязательно проведите некоторое время не только на форумах, но и просмотрите различные блоги и учебные пособия, например на hackaday.com, learn.sparkfun.com или learn. adafruit. .com. Лучшие блоги и учебные пособия – это те, которые объясняют не только то, как писать код, но и то, что делает плата и как ее использовать. Таких онлайн-справок немного, но те, что с этих трех сайтов, являются одними из самых лучших1. Также посмотрите несколько видеороликов по данной теме. На некоторые из них стоит потратить время, особенно если они от хороших ребят из Adafruit или SparkFun. Протокол I2C Протокол Inter-integrated Circuit (I2C) – пожалуй, самый распространенный протокол, который вы найдете на внешних модулях. Мы несколько раз встречали этот термин в предыдущих главах и поэтому знаем, что это только протокол связи. Итак, что это такое? Что такое I2C? I2C – это быстрый цифровой протокол, использующий два провода (плюс питания и общий) для чтения данных из схем или устройств. Протокол предназначен для использования нескольких устройств (ведомых) с одним главным устройством (ведущим), в качестве которого выступает плата MicroPython. Каждый модуль с протоколом I2C будет иметь свой собственный адрес (идентификатор), который необходимо использовать в драйвере для связи с устройством. СОВЕТ См. https://learn.sparkfun.com/tutorials/i2c для более подробного обсуждения I2C2. 1 2 Лучшие отечественные интернет-магазины («Чип и дип», iarduino, «Амперка» и др.), как правило, предоставляют документацию от производителя, а часто также ссылки на инструкцию по применению, содержащую сведения о базовых библиотеках. – Прим. перев. На русском языке см., например https://3d-diy.ru/wiki/arduino-moduli/interfeys-peredachi-dannykh-i2c. Как правило, в описаниях I2C забывают указать допустимые пределы номиналов подтягивающих резисторов для линий SDA и SCL, так как они уже установлены на подключаемых модулях. Резисторы, вообще говоря, рассчитываются по довольно сложной методике (в случае если необходимо обеспечить максимальную скорость обмена), но обычно необходимый и достаточный их номинал составляет от 1 до 10 кОм. На подключаемых платах он чаще всего составляет 4,7–5,1 кОм. По этой причине более четырех-пяти I2C-плат включать параллельно не рекомендуется – во избежание перегрузки выводов портов контроллера (это в большей степени касается 32-разрядных чипов из числа рассматриваемых в этой книге и в меньшей – классического Arduino на основе AVR-контроллеров, у которых порты относительно более мощные). – Прим. перев.
212  Низкоуровневая поддержка оборудования Обзор Давайте рассмотрим пример использования модуля с интерфейсом I2C. В этом примере мы хотим использовать RGB-датчик от Adafruit (www.adafruit. com/product/1334) для считывания цвета объектов. Да, вы можете заставить свой Pico видеть в цвете! Код представит четыре значения, считанных с датчика. Мы увидим значения красной (R), зеленой (G) и синей (B) составляющих, а также значение общей яркости (здесь обозначаемое C1). Комбинация значений красного, зеленого и синего определяет цвет. Вы можете использовать элемент управления выбором цвета на одном из веб-сайтов (например, www.rapidtables. com/web/color/RGB_Color.html), чтобы увидеть цвет. Данный RGB-датчик не обеспечит 100%-ного совпадения цветов, но вы можете быть удивлены, насколько хорошо он различает цвета. Начнем! Необходимые компоненты Не волнуйтесь, если у вас нет или вы не хотите приобретать плату Adafruit RGB Sensor (хотя она стоит недорого). Этот пример представлен в качестве руководства по работе с внешними модулями I2C вообще. Мы будем использовать другую плату I2C в одном из примеров проектов далее в книге. На рис. 6.1 показан датчик Adafruit RGB. Обратите внимание, что этот датчик поставляется без припаянного разъема, поэтому вам нужно будет припаять разъем к коммутационной плате, прежде чем вы сможете использовать его с Pico. Рис. 6.1  Датчик Adafruit RGB (с разрешения adafruit.com) 1 Почему-то для этого датчика значение интегральной яркости Adafruit называет словом «clear» (см. код примера далее). – Прим. перев.
Использование внешних модулей  213 Настройка оборудования Подключение платы датчика также очень простое, поскольку нам нужны только соединения питания, заземления, последовательной синхронизации (SCL) и последовательной передачи данных (SDA). SCL – тактовый сигнал, а SDA – сигнал данных. Эти контакты помечены на плате Pico (или в документации), а также на подключаемой плате модуля. При подключении платы убедитесь, что требования к питанию совпадают: некоторые коммутационные платы могут потреблять 5 В, но многие ограничены 3 или 3,3 В. Если у вас есть какие-либо сомнения, посетите веб-сайт поставщика. Нам нужно только соединить контакты 3 В, GND, SDA, SCL и LED. Вывод LED используется для включения светодиода подсветки на модуле, освещающего измеряемое поле. Оставим его включенным на десять секунд, чтобы было время прочитать значение цвета и затем отобразить его. Затем подождем еще пять секунд, чтобы снять следующее показание. Но чтобы это заработало, нам нужно будет подключить плату к Pico. Если вы заказали Pico с разъемами или припаяли к Pico свои собственные разъемы, мы можем использовать макетную плату для размещения Pico и использовать провода, называемые перемычками, для подключения контактов GPIO Pico к контактам на модуле. ПРИМЕЧАНИЕ Мы обсудим макетные платы и их использование более подробно в следующей главе. Рис. 6.2  Подключение RGB-датчика Соединения, которые необходимо сделать, показаны в табл. 6.2, где в первых трех столбцах показаны выводы Pico с описанием и обозначением, но-
214  Низкоуровневая поддержка оборудования мером физического вывода и номером GPIO, а в последнем столбце – вывод на подключаемой плате. Напомним, физические выводы пронумерованы 1–20 слева от разъема USB, начиная сверху, и 21–40 справа, начиная снизу (см. рис. 3.3). Таблица 6.2. Соединения для датчика RGB Pico Функция (обозначение) Физический контакт Номер GPIO GP15 Выход 20 I2C SDA (I2C0 SDA) 11 GP8 GP9 I2C SDC (I2C0 SDL) 12 3,3 В (3V3 OUT) 36 N/A Общий (GND) 38 N/A RGB-датчик Название контакта LED SDA SCL 3V3 GND Написание кода Подключив оборудование, отложите его в сторону. Нам нужно скачать драйвер и скопировать его на плату, прежде чем мы сможем экспериментировать дальше. Драйвер можно скачать на GitHub по адресу https://github.com/ adafruit/micropython-adafruit-tcs34725. Это полностью рабочий, протестированный драйвер, который демонстрирует, насколько просто использовать платы с интерфейсом I2C. ПРИМЕЧАНИЕ Компания Adafruit отказалась от этой библиотеки, пытаясь сосредоточиться на своей версии MicroPython под названием CircuitPython. Но не волнуйтесь: библиотека до сих пор работает очень хорошо, просто вряд ли мы увидим какие-либо обновления кода. Итак, как нам найти адрес нашего I2C-модуля? Напомним, что шина I2C требует, чтобы каждое устройство имело уникальный адрес. Драйвер I2C использует этот адрес, чтобы узнать, с каким устройством оно взаимодействует, а само устройство будет распознавать сообщения только для этого конкретного адреса. Мы можем проверить либо документацию, либо код библиотеки. Если вы откроете загруженную библиотеку, то сможете прочитать ее и просмотреть код инициализации (конструктор), чтобы узнать, какой адрес использует библиотека. В данном случае мы находим в библиотеке адрес 0x29, но поскольку адрес является параметром, вы можете переопределить его, если у вас есть другой модуль для того же датчика RGB, но по другому адресу. Это означает, что вы можете использовать более одного датчика RGB с одним и тем же драйвером! Чтобы загрузить драйвер, сначала перейдите по адресу https://github.com/ adafruit/micropython-adafruit-tcs34725, затем нажмите кнопку Download (Загрузить), а потом кнопку Download Zip (Загрузить zip). Как только файл загрузится, разархивируйте его. В полученной папке вы должны найти файл с именем tcs34725.py. Это модуль кода драйвера. Когда все будет готово, ско-
Использование внешних модулей  215 пируйте модуль на Pico и поместите его в корневую папку (ту же папку, что и пример кода). Теперь, когда драйвер скопирован на плату, мы можем написать код. В этом примере мы настроим соединение I2C с платой и запустим цикл для считывания значений с датчика. Звучит просто, но здесь есть небольшая хитрость. Мы воздержимся от длительного обсуждения кода и вместо этого предложим некоторые ключевые аспекты, позволяющие вам самостоятельно прочитать код и увидеть, как он работает. Ключевыми компонентами являются настройка I2C, датчика, контакта для управления светодиодом и считывания данных с датчика. Светодиод на плате можно включать и выключать, устанавливая на выводе высокий (включен) или низкий (выключен) уровень. Код настройки I2C показан ниже. Здесь мы инициируем объект, затем вызываем функцию init(), переводя шину в режим ведущего. Функция scan() возвращает список адресов, найденных на шине. Затем мы можем распечатать адреса устройств. Обратите внимание, что мы также определяем контакты для операций SDA и SCL I2C. СОВЕТ Если вы видите пустой набор на выходе вместо списка адресов, значит, подключение I2C неправильное. Проверьте его и попробуйте ввести код еще раз. # Setup the I2C - easy, yes? sda = Pin(8) scl = Pin(9) i2c = SoftI2C(sda=sda,scl=scl,freq=400000) print("I2C Devices found:", end="") for addr in i2c.scan(): print("{0} ".format(hex(addr))) print("") Обратите внимание, что здесь мы используем нечто под названием SoftI2C. Это специальная версия библиотеки I2C, поддерживающая аналогичный способ связи с модулем. Как оказалось, не все устройства I2C будут коррект­ но работать со встроенной реализацией I2C на Pico. Чтобы использовать встроенный I2C, используйте библиотеку I2C из модуля machine аналогичным образом. Единственное отличие, помимо имени, – это первый параметр, который сообщает I2C, что нам нужно ведущее соединение через порт I2C0. #i2c = I2C(0,sda=sda,scl=scl,freq=400000) Рекомендуется сначала попробовать встроенный I2C, а если он не сработает, обратиться к библиотеке SoftI2C. Это связано с тем, что встроенный I2C намного быстрее, чем программная реализация. В последующих главах мы увидим конкретные примеры использования I2C и SoftI2C. Следующая часть – подключение самого датчика. Драйвер делает это легко. Все, что нам нужно сделать, – это передать функцию конструктора I2C, как показано в этом примере:
216  Низкоуровневая поддержка оборудования # Setup the sensor sensor = tcs34725.TCS34725(i2c) Настройка вывода светодиода также проста. Все, что нам нужно сделать, – это вызвать конструктор класса Pin(), передав номер GPIO (15) и установив его в режим на выход следующим образом: # Setup the LED pin led_pin = Pin(15, Pin.OUT) led_pin.value(0) Наконец, мы читаем данные с датчика с помощью функции sensor.read(), передающей значение True, которое сообщает драйверу вернуть значения RGBC. Затем мы распечатаем их по порядку. Пример кода можно загрузить с сайта этой книги, имя файла – adafruit_rgb_ sensor.py. Если вы хотите создать файл самостоятельно, можете использовать это имя и сохранить его на своем Pico. В лис­тин­ге 6.4 показан готовый код. Уделите несколько минут прочтению, чтобы понять, как это работает. Листинг 6.4  Использование датчика Adafruit RGB # # MicroPython for the IoT Second Edition - Chapter 6 # # Example of using the I2C interface via a driver # for the Adafruit RGB Sensor tcs34725 # # Requires library: # https://github.com/adafruit/micropython-adafruit-tcs34725 # from machine import I2C, SoftI2C, Pin import sys import tcs34725 import utime # Method to read sensor and display results def read_sensor(rgb_sense, led): sys.stdout.write("Place object in front of sensor now...") led.value(1) # Turn on the LED utime.sleep(5) # Wait 5 seconds sys.stdout.write("reading.\n") data = rgb_sense.read(True) # Get the RGBC values print("Color Detected: {") print(" Red: {0:03}".format(data[0])) print(" Green: {0:03}".format(data[1])) print(" Blue: {0:03}".format(data[2])) print(" Clear: {0:03}".format(data[3])) print("}\n") led.value(0) # Setup the I2C - easy, yes?
Использование внешних модулей  217 sda = Pin(8) scl = Pin(9) i2c = SoftI2C(sda=sda,scl=scl,freq=400000) print("I2C Devices found:", end="") for addr in i2c.scan(): print("{0} ".format(hex(addr))) print("") # Setup the sensor sensor = tcs34725.TCS34725(i2c) # Setup the LED pin led_pin = Pin(15, Pin.OUT) led_pin.value(0) print("Reading object color every 10 seconds.") print("When LED is on, place object in front of sensor.") print("Press CTRL-C to quit.") while True: utime.sleep(10) # Sleep for 10 seconds read_sensor(sensor, led_pin) # Read sensor and display values Получив код, вы можете скопировать его на свою плату аналогично тому, как мы это сделали для драйвера. Осталось только запустить пример и протестировать его. Выполнение кода Скопировав код в Pico, запустите его из Thonny. В лис­тин­ге 6.5 показан пример работающего кода. Обратите внимание, что вы получите разные результаты для каждого тестируемого объекта в смеси значений RGB. Листинг 6.5  Результат использования датчика Adafruit RGB I2C Devices found:0x29 Reading Colors every 10 seconds. When LED is on, place object in front of sensor. Press CTRL-C to quit. Place object in front of sensor now...reading. Color Detected: { Red: 057 Green: 034 Blue: 032 Clear: 123 } Place object in front of sensor now...reading. Color Detected: { Red: 054 Green: 069 Blue: 064 Clear: 195 } Place object in front of sensor now...reading.
218  Низкоуровневая поддержка оборудования Color Detected: { Red: 012 Green: 013 Blue: 011 Clear: 036 } ... Для упражнения вы можете взять эти значения с датчика и сопоставить их со свечением светодиода RGB. См. пример проекта GitHub по адресу https:// github.com/JanBednarik/micropython-ws2812. Можете попробовать заняться этим после прочтения следующего раздела, посвященного SPI. Последовательный периферийный интерфейс SPI Последовательный периферийный интерфейс (Serial Peripheral Interface, SPI) предназначен для отправки и получения данных между двумя устройствами с использованием отдельной линии для каждого направления. То есть он использует две линии данных, линию тактового сигнала и вывод выбора подчиненного устройства. Таким образом, для двунаправленной связи требуется шесть соединений (считая питание и общий провод) или пять только для чтения или записи. Некоторым устройствам SPI может потребоваться седьмой контакт, называемый линией сброса. СОВЕТ См. https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi для более подробного обсуждения SPI. Обзор Давайте рассмотрим пример использования модуля с интерфейсом SPI. В этом примере мы хотим использовать плату усилителя термопары Adafruit MAX31855 (www.adafruit.com/product/269) и датчик термопары типа K (www. adafruit.com/product/270) для измерения высоких температур. Он также может показывать низкую комнатную температуру, так что не волнуйтесь. Для проверки вам не придется засовывать его в обогреватель или духовку! Фактически мы собираемся использовать этот пример, чтобы показать, как легко проделать одно из наиболее распространенных измерений – определение температуры. После запуска программы вы можете просто прикоснуться к термопаре и наблюдать, как значения изменяются, когда она нагревается, и снова, когда вы ее отпускаете. Познавательный проект! Необходимые компоненты Не волнуйтесь, если у вас нет или вы не хотите приобретать подключаемую плату Adafruit Thermocouple Amplifier MAX31855 (хотя она стоит недорого). Этот пример представлен в качестве руководства по работе с платами, под-
Использование внешних модулей  219 ключаемыми по SPI. На рис. 6.3 показаны усилитель термопары Adafruit и датчик типа K от Adafruit. Рис. 6.3  Плата усилителя термопары Adafruit и датчик типа K (с разрешения adafruit.com) Датчик можно использовать для измерения высоких температур как размещением поблизости1, так и через плотный контакт. Датчик может считывать температуру в диапазоне от –200 °C до +1350 °C с шагом 0,25 градуса. Одно из возможных применений этого датчика – считывание температуры сопел 3D-принтеров или других подобных устройств с высокой тепловой мощностью. Следует отметить, что коммутационная плата поставляется в разобранном виде, поэтому вам потребуется припаять разъем и клеммную колодку. 1 Неверное утверждение. Более-менее точно измерить температуру любого объекта можно только через плотный контакт, причем желательно во внутренней полости объекта, без возможности рассеяния тепла от поверхности датчика. Вспомните, как медики измеряют температуру человеческого тела, помещая термометр в какуюлибо полость или под мышку. Попытка измерить температуру на поверхностных покровах тела (не говоря уж о «размещении поблизости», как утверждает автор) во всех случаях терпит неудачу – в лучшем случае вы получаете серию показаний, которая коррелирует с реальной температурой, но далеко не равна ей. В случае же измерения высоких температур, как в основных применениях термопар, ошибка будет еще выше из-за большего рассеяния тепла при значительных перепадах между датчиком и объектом. Так, попытка измерять температуру, например, внут­ ри термошкафа приложением термопары к его корпусу заведомо даст ложный, значительно заниженный результат. – Прим. перев.
220  Низкоуровневая поддержка оборудования Настройка оборудования Теперь давайте посмотрим, как подключить плату к нашему Pico. Мы будем использовать только пять проводов, поскольку считываем данные лишь с датчика на плате. Для этого требуется подключение к питанию (3V3), земле (GND), главному входу (MOSI), тактовому сигналу (CLK) и выбору микросхемы (CS). Мы получаем информацию только от датчика, поэтому контакт MISO (передача) не требуется. На рис. 6.4 показаны необходимые соединения. Рис. 6.4  Подключение модуля термопары Adafruit Соединения, которые мы будем использовать, показаны в табл. 6.3, где в первых трех столбцах показаны выводы Pico с описанием, номером физического вывода и номером GPIO, а в последнем столбце – соответствующий вывод на подключаемой плате. Напомним, физические выводы пронумерованы 1–20 слева от разъема USB, начиная сверху, и 21–40 справа, начиная снизу (см. рис. 3.3). Таблица 6.3. Соединения для MAX31855 Pico Функция (обозначение) Общий (GND) 3,3 В (3V3 OUT) SPI RX/MOSI (SPI0 RX) SPI CS (SPI0 CSn) SPI CLK (SPI0 SCK) Физический контакт 38 36 6 2 9 Номер GPIO N/A N/A GP4 GP1 GP6 RGB-датчик Название контакта GND 3V3 SD0 CS CLK
Использование внешних модулей  221 Теперь давайте посмотрим на составление кода. Написание кода В этом примере мы не собираемся использовать драйвер; скорее, мы увидим, как читать напрямую с платы модуля с помощью SPI. Для этого мы сначала настраиваем экземпляр объекта интерфейса SPI, затем выбираем вывод, который будет использоваться для выбора микросхемы1 (также называемого выбором ведомого устройства). После этого все, что нам нужно сделать, – это прочитать данные и интерпретировать их. Мы прочитаем датчик в цикле и напишем функцию для преобразования данных. Это самая сложная часть. В этом примере показано, что должны сделать авторы драйверов, чтобы упростить использование устройства. В этом случае мы должны считать данные с промежуточной платы и интерпретировать их. Мы могли бы просто прочитать необработанные данные, но это не имело бы никакого смысла, поскольку они, во-первых, находятся в двоичной форме, во-вторых, представляют совсем не температуру, а некий пропорциональный ей код. Таким образом, мы можем позаимствовать у Adafruit некоторый код, который считывает необработанные данные и преобразует их. Функция называется normalize_data() и работает следующим образом: выполняет сдвиг битов и арифметические операции для преобразования необработанных данных в значения в градусах Цельсия. # Create a method to normalize the data into degrees Celsius def normalize_data(data): temp = data[0] << 8 | data[1] if temp & 0x0001: return float('NaN') temp >>= 2 if temp & 0x2000: temp -= 16384 return (temp * 0.25) Эта информация взята из таблицы данных модуля, но приятные ребята из Adafruit облегчили нам задачу. Настроить класс SPI легко. Мы инициируем объект SPI, используя конструктор класса, передающий параметр SPI (равный нулю для первой аппаратной реализации SPI). Другие параметры сообщают классу SPI о настройке выводов SCK, MISO и MOSI (даже если мы не используем вывод MOSI) и устанавливают скорость передачи данных (полярность и фазу можно найти в таблице данных). Мы также устанавливаем вывод CS и включаем его (устанавливаем на высокий уровень) после инициализации библиотеки SPI. Ниже показан код для активации интерфейса SPI: 1 Название вывода CS расшифровывается как chip select, дословно «выбор микросхемы». – Прим. перев.
222  Низкоуровневая поддержка оборудования ... spi_cs = Pin(1) spi = SPI(0, baudrate=1000000, sck=Pin(6), miso=Pin(4), mosi=Pin(3)) spi_cs.high() ... Теперь давайте посмотрим на готовый код. Если вы следуете примеру кода, загруженному с сайта книги, имя файла – adafruit_thermocouple.py. Если вы хотите создать файл самостоятельно, вы можете использовать это имя и сохранить его на своем Pico. В лис­тин­ге 6.6 показан полный код для использования платы усилителя термопары от Adafruit. Листинг 6.6  Пример модуля термопары Adafruit Listing 6-6. The Adafruit Thermocouple Module Example # # MicroPython for the IoT Second Edition - Chapter 6 # # Example of using the SPI interface via direct access # for the Adafruit Thermocouple Module MAX31855 # from machine import Pin, SPI import utime # Create a method to normalize the data into degrees Celsius def normalize_data(data): temp = data[0] << 8 | data[1] if temp & 0x0001: return float('NaN') temp >>= 2 if temp & 0x2000: temp -= 16384 return (temp * 0.25) spi_cs = Pin(1) spi = SPI(0, baudrate=1000000, sck=Pin(6), miso=Pin(4), mosi=Pin(3)) spi_cs.high() # read from the chip print("Reading temperature every second.") print("Press CTRL-C to stop.") while True: spi_cs.low() utime.sleep(1) print("Temperature is {:05.2F} C".format(normalize_data(spi.read (4)))) spi_cs.high() Выполнение кода На этом этапе вы можете выполнить аппаратные подключения и подключить Pico. Затем вы можете скопировать файл на Pico и запустить его. Дайте ему
Итоги  223 проработать несколько показаний, затем попытайтесь осторожно захватить двумя пальцами серебристую часть (дальний конец) термопары. Вы должны увидеть изменение температуры. Вы можете отпустить термопару и увидеть, как температура вернется к температуре, близкой к комнатной. Reading temperature every second. Press CTRL-C to stop. Temperature is 24.50 C Temperature is 24.50 C Temperature is 24.25 C Temperature is 24.25 C Temperature is 25.75 C Temperature is 25.50 C Temperature is 25.75 C Temperature is 26.25 C Temperature is 26.00 C Temperature is 26.50 C Temperature is 26.50 C Temperature is 27.00 C Temperature is 27.25 C Temperature is 27.00 C Temperature is 27.50 C Temperature is 27.50 C Temperature is 27.00 C ... Запустив пример, вы должны увидеть, что он выдает значения в градусах Цельсия. Если вы видите 00.00 или «NaN», скорее всего, у вас неправильно подключен интерфейс SPI. Проверьте соединения по рисунку выше. Если вы видите значения, но они уменьшаются, когда вы подвергаете наконечник термопары нагреву, вам необходимо поменять местами провода термопары. Обязательно сначала отключите питание платы, чтобы не повредить датчик, плату или Pico! Итоги Доступ к низкоуровневому оборудованию через встроенные функции – это то, где начинается истинная элегантность, а в некоторых случаях и сложность использования MicroPython. Учитывая, что доступные платы различаются, неудивительно, что низкоуровневая поддержка в прошивке также различается. Таким образом, при планировании IoT-проектов MicroPython мы должны учитывать, что хотим сделать и поддерживает ли это наша плата (и ее прошивка). Нам также необходимо знать, какие внешние модули и устройства мы хотим использовать и есть ли драйверы или другие библиотеки для доступа к ним. Большинству внешних модулей с интерфейсами I2C или SPI потребуются драйверы той или иной формы.
224  Низкоуровневая поддержка оборудования В этой главе мы рассмотрели некоторые низкоуровневые возможности встроенного ПО и специализированную поддержку набора микросхем RP2040, а также уникальные функции Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как мы обнаружили, именно здесь код становится очень специа­ лизированным. Как мы видели, иногда возникает вопрос выбора другой библиотеки для импорта, но бывает, классы, функции и даже способы использования функций различаются от одной платы к другой. Мы также привели несколько примеров кода и отдельных фрагментов, которые предназначены для того, чтобы вы могли увидеть, как все это делается, а не для проектов, которые можно реализовать самостоятельно (хотя мы приветствуем и поощряем это делать). В последующих главах мы увидим больше практических проектов с большей детализацией. В следующей главе мы сделаем небольшой экскурс в виде небольшого урока по электронике. Если вы никогда раньше не работали с электроникой, следующая глава предоставит вам информацию, необходимую для выполнения проектов в этой книге, и подготовит вас к новому увлекательному хобби – созданию IoT-проектов на MicroPython!
Глава 7 Электроника для начинающих Если вы новичок в работе с аппаратным обеспечением и не имеете практического опыта работы с электроникой, вам может быть интересно, как можно реализовать проекты, описанные в этой книге. Проекты в этой книге покажут вам, как соединять различные электронные компоненты с платой MicroPython. То есть вы можете выполнять эти проекты без дополнительных навыков и опыта. Однако если вы хотите знать, что делают компоненты, вам понадобится немного больше информации, чем «подключить этот конец сюда». Особенно это актуально, если что-то пойдет не так. Более того, если вы хотите создавать проекты самостоятельно, вам необходимо знать достаточно о том, как работают компоненты, чтобы успешно завершить ваш проект – будь то выполнение примеров в этой книге или примеров, найденных в других местах в интернете. К счастью, чтобы научиться работать с электроникой, не нужно высшее тео­ретическое образование. Вы можете узнать довольно много о работе с электроникой на уровне любителя, не тратя месяцы или годы исследований1. Чтобы добиться успеха на базовом уровне, вам нужно будет знать немного больше, чем просто то, как соединять компоненты вместе. Вместо того чтобы попытаться представить всеобъемлющее руководство по электронике, которое заняло бы несколько томов, в этой главе представлен обзор электроники для тех, кто хочет работать с типами электронных компонентов, обычно встречающихся в проектах интернета вещей. Я включаю обзор некоторых основ, описания общих компонентов и обзор датчиков. Если 1 Однако ничто не заменит формального обучения! Если вы хотите изучить электронику помимо сведений в этой главе, то можете рассмотреть возможность формального обучения или курса для самостоятельного обучения, как описано далее во врезке «Я хочу узнать больше!». – Прим. авт.
226  Электроника для начинающих вы новичок в электронике, эта глава даст вам дополнительную информацию, необходимую для понимания компонентов, описанных в данной книге. Если у вас есть опыт работы с электроникой на уровне любителя или энтузиаста либо у вас есть опыт или формальное обучение в области электроники, вы можете просмотреть эту главу или прочитать разделы с темами, по которым вам может потребоваться повышение квалификации. Начнем с изучения основ электроники. Еще раз: это ни в коем случае не учебник, охватывающий все, что нужно знать, но он приведет вас к тому моменту, когда проекты обретут смысл в том, как в них соединяются и используются компоненты. Основы В этом разделе представлен краткий обзор некоторых наиболее распространенных инструментов и методов, которые вам понадобятся при работе с электроникой. Как вы увидите, вам понадобятся только самые базовые инструменты, а навыки и методы освоить несложно. Однако прежде чем мы перейдем к этим вопросам, давайте рассмотрим некоторые инструменты, которые вам понадобятся для работы над проектами интернета вещей. Инструменты Подавляющее большинство инструментов, которые вам понадобятся для создания проектов интернета вещей, – это обычные ручные инструменты (отвертки, небольшие гаечные ключи, плоскогубцы и т. д.). Для более крупных проектов или создания корпусов вам могут понадобиться дополнительные приспособления, но я сосредоточусь только на ручных инструментах для создания проектов. Ниже приведен список инструментов, которые я рекомендую для начала:             макетная плата; провода (перемычки) для макетной платы; пинцет, безопасный для электростатического разряда; штатив для плат «третья рука»; мультиметр; малогабаритные пассатижи (плоскогубцы) с узкими губками (длинногубцы); отвертки разных размеров (в т. ч. миниатюрные «часовые»); припой; паяльник; средство для удаления припоя (отсос для припоя); инструменты для зачистки проводов; чемодан или ящик для хранения инструментов.
Основы  227 Вы не ошибетесь, если предпочитаете купить полный набор инструментов для электроники, например у SparkFun или Adafruit1. Вы часто можете найти комплекты электроники в магазинах электроники крупных брендов и в торговых центрах по благоустройству дома. Если вам посчастливилось жить рядом с магазином, в котором продаются электронные компоненты и комплекты, вы можете найти практически любой электронный инструмент. В большинстве комплектов электроники есть все необходимые ручные инструменты. Некоторые даже идут в комплекте с мультиметром, но их чаще приходится покупать отдельно. Большинство инструментов в списке не нуждаются в каких-либо объяснениях, кроме как сказать, что вам следует покупать лучшие инструменты, которые позволяет ваш бюджет. В следующих подразделах описаны некоторые инструменты, используемые для выполнения специальных задач, таких как зачистка проводов, пайка и измерение напряжения и тока. Мультиметр Мультиметр – один из тех инструментов, которые вам обязательно понадобятся при создании IoT-проектов. Он также понадобится вам для выполнения практически любого электрического ремонта в ваших устройствах. Доступно множество различных мультиметров по ценам от недорогих базовых до сложных, многофункциональных и невероятно дорогих. Для большинства IoT-проектов вам понадобится только базовая функциональность. Однако если вы планируете создать более одного IoT-решения или хотите собрать собственную электронику, возможно, вам захочется инвестировать немного больше в более сложный мультиметр. На рис. 7.1 показан базовый цифровой мультиметр слева и профессиональный мультиметр от BK Precision справа. Обратите внимание, что профессиональный измеритель имеет более детальные настройки и больше функций. Повторим, что вам, вероятно, не понадобится больше, чем базовые функции. Вам нужно будет измерять как минимум напряжение, ток и сопротивление. Какой бы измеритель вы ни купили, убедитесь, что он имеет режимы измерения переменного и постоянного напряжений, проверки целостности цепи (функция «прозвонки» со звуковым сигналом) и проверки сопротивления. Использование мультимет­ ра описывается в разделе ниже. СОВЕТ К большинству мультиметров, включая недорогие, прилагается небольшой буклет с инструкциями, в котором показано, как измерять напряжение, сопротивление и другие параметры, а также описаны различные функции устройства. 1 Среди отечественных магазинов хорошие наборы инструментов для электроники продаются, например, в «Чип-дип». Однако полагаться на такое приобретение не следует: в готовых наборах обязательно чего-то не хватит, что-то окажется лишним, а что-то окажется некачественным или неудобным для применения. Потому предпочтительно приобретать инструменты и материалы по отдельности, тщательно рассматривая альтернативные предложения по каждой позиции. – Прим. перев.
228  Электроника для начинающих Рис. 7.1  Цифровые мультиметры Паяльник Ни для одного из проектов в этой книге не потребуется паяльник, поскольку для размещения и соединения компонентов мы будем использовать макетную плату. Однако если вы планируете создать простое IoT-решение, в котором вам нужно будет припаять провода и, возможно, несколько разъемов, все, что вам понадобится, – это простой паяльник из магазина электроники. С другой стороны, если вы планируете собирать свою собственную электронику, возможно, вам стоит подумать о приобретении хорошего профессионального паяльника, например Hakko1. Профессиональные модели оснащены функциями, которые позволяют устанавливать температуру жала, имеют более широкий набор насадок и, как правило, служат намного дольше. На рис. 7.2 показан широко используемый вариант начального уровня от RadioShack. 1 В отечественных условиях недорогие качественные паяльники продаются, например, под торговой маркой Rexant. Для тонких работ (пайка разъемов и тонких проводов) рекомендуется выбирать мощность паяльника в пределах 20–25 Вт. Более мощные (45–60 Вт) могут понадобиться отдельно для пайки более толстых проводников и деталей (например, для пайки сетевых проводов). Не приобретайте отечественных паяльников (типа ЭПСН) с необлуженным медным жалом – намучаетесь! С другой стороны, дорогая профессиональная паяльная станция с точной регулировкой температуры жала, как правило, для любительских нужд оказывается избыточной. – Прим. перев.
Основы  229 Рис. 7.2  Паяльник начального уровня На рис. 7.3 показан профессиональный паяльник Hakko. Рис. 7.3  Профессиональный паяльник СОВЕТ Для достижения наилучших результатов выбирайте припой с низким содержанием свинца в диапазоне 37–40 %1. Если вы используете профессиональный паяльник, отрегулируйте температуру в соответствии с температурой плавления припоя (указанной на этикетке). 1 Для ручной любительской пайки рекомендуется отечественная марка припоя ПОС63 (цифра приблизительно показывает содержание олова). Температура плавления 183 градуса, оптимальная температура жала 210–230 градусов. Не употребляйте для любительских работ экологичный бессвинцовый припой – его температура плавления гораздо выше и ручная пайка сопряжена с рядом трудностей! – Прим. перев.
230  Электроника для начинающих Нужно ли мне учиться паять? Если вы не умеете паять или давно не пользовались паяльником, возможно, вам стоит почитать книгу Марка де Винка (Marc de Vinck) «Getting Started with Soldering: A Hands-On Guide to Making Electrical and Mechanical Connections» (O’Reilly Media, 2017)1. Инструменты для зачистки проводов Существует несколько типов инструментов для зачистки проводов. На самом деле существует, вероятно, дюжина или больше их конструкций. Но есть два основных типа: те, которые захватывают и разрезают изоляцию, когда вы снимаете ее с провода, и те, которые захватывают, разрезают и удаляют изоляцию за одно действие. Первый тип более распространен и, при некоторой практике, отлично справляется с большинством мелких работ (например, ремонт оборванного провода), но второй тип позволяет выполнять более крупные работы – например, подключение электроники из оголенного провода (без готовых разъемов) – гораздо аккуратнее и быстрее. Как вы понимаете, первый тип значительно дешевле. На рис. 7.4 показаны оба типа инструментов для зачистки проводов. И то, и другое – хороший выбор. Рис. 7.4  Инструменты для зачистки проводов 1 На русский язык эта книга не переводилась, но в русскоязычном интернете су­ ществует огромное количество ресурсов на тему «пайка для начинающих», причем адаптированных под отечественные реалии. – Прим. перев.
Основы  231 Штатив для плат «третья рука» Есть еще один инструмент, который вам может понадобиться, особенно если вам нужно паять: он называется «держатель вспомогательная рука» или «штатив третья рука». Большинство из них имеют пару зажимов типа «крокодил» для удержания проводов, печатных плат или компонентов во время пайки. На рис. 7.5 показан пример простого штатива «третья рука». Рис. 7.5  Штатив «третья рука» Теперь давайте рассмотрим некоторые навыки, которые могут вам понадобиться при работе с продвинутыми проектами интернета вещей. Защита от статического электричества (ESD) Вам следует позаботиться о том, чтобы ваше тело, рабочее место и компоненты проекта были заземлены, дабы избежать электростатического разряда (ESD). ESD может повредить вашу электронику навсегда. Лучший способ избежать этого – использовать заземляющий браслет, который обхватывает запястье и прикрепляется к антистатическому коврику1. Давайте посмотрим, как использовать инструмент, который вы, вероятно, будете использовать чаще, чем любой другой при изучении электроники, – мультиметр. 1 См. сноску 3 на стр. 83. – Прим. перев.
232  Электроника для начинающих Использование мультиметра Навыки электротехники, необходимые для IoT-проектов, могут варьироваться от подключения проводов к макетной плате (как мы видели на примере проектов до сих пор) до необходимости пайки компонентов или печатных плат (PCB). Независимо от того, нужно ли вам паять электронику, вам понадобится простой мультиметр для измерения сопротивления и проверки значений напряжения и тока. Мультиметр – очень полезный инструмент для любого любителя электроники и совершенно необходимый любому достойному энтузиасту. Типичный мультиметр имеет цифровой дисплей1 (обычно ЖК или аналогичный цифровой дисплей), переключатель с круглой шкалой и два или более гнезда для подключения измерительных проводов со щупами на концах. Большинство мультиметров имеют отдельные гнезда для более слабых токов (которые вы будете использовать чаще всего) и гнезда для больших токов. В измерительных проводах красный цвет используется для положительного контакта, а черный – для отрицательного (обычно совпадающего с общим проводом). Гнездо общего провода – это место, куда вы подключаете черный измерительный провод, и оно часто обозначается знаком минус или COM («общий»). Какое из других гнезд вы используете, будет зависеть от того, что вы измеряете. На шкале следует отметить множество наборов значений (шкал) для разных величин. Например, вы увидите набор для сопротивлений (помечен значком Ω), обычно по два набора значений для силы тока и напряжений. Набор значений напряжений, обозначенный буквой V с прямой и пунктирной линиями сверху, предназначен для постоянного тока, тогда как диапазон, обозначенный буквой V с волнистой линией, предназначен для переменного тока. Диапазоны силы тока отмечены таким же образом. На рис. 7.6 показан крупный план шкалы мультиметра, на которой имеются упомянутые наборы значений. СОВЕТ Когда мультиметр не используется, обязательно выключайте его через положение OFF или отдельной кнопкой выключения, если она есть2. С помощью мультиметра можно многое сделать. Вы можете проверить напряжение, измерить сопротивление и даже проверить целостность контакта. Большинство простых мультиметров выполняют эти функции. Однако некоторые мультиметры имеют гораздо больше функций: такие как измерение емкости конденсаторов или возможность измерения частоты (Hz). 1 2 Старые мультиметры имеют аналоговую стрелочную шкалу. Вы все еще можете найти их (даже в продаже среди самых дешевых), если хотите немного атмосферы старой школы. – Прим. авт. Большинство современных мультиметров имеют функцию автоматического «засыпания» через некоторое время (обычно 5–10 минут) при неиспользовании. Независимо от этого, кнопка ручного выключения (или положение OFF на шкале) позволяет выключать мультиметр полностью – во избежание пустого расхода батареи. – Прим. перев.
Основы  233 Вольты AC Амперы AC Вольты DC Амперы DC «Прозвонка» Сопротивление Рис. 7.6  Шкала мультиметра (типовой пример) Давайте посмотрим, как мы можем использовать мультиметр для выполнения наиболее распространенных задач, которые нам понадобятся в IoTпроектах: проверка целостности цепи, измерение напряжения в цепи постоянного тока, измерение сопротивления и измерение тока. Тестирование целостности цепи («прозвонка») Мы проверяем целостность цепи, чтобы определить, есть ли путь для движения тока: то есть правильно ли соединены наши провода и компоненты. Например, вы можете проверить, надежно ли сращен провод и к тому ли контакту он подключен. Чтобы проверить целостность цепи, поверните шкалу мультиметра в положение, отмеченное звуковым символом, колокольчиком или треугольником со стрелкой через него. Подключите черный измерительный провод к гнезду COM, а красный измерительный провод к гнезду, отмеченному значками VΩHz или аналогично. Теперь вы можете прикоснуться концами измерительных проводов друг к другу, чтобы услышать звуковой сигнал. Некоторые мультиметры не издают звукового сигнала, а вместо этого могут отображать цифру «1» или что-то подобное, указывающее на целостность цепи (проверьте в руководстве, как ваш мультиметр показывает наличие ненарушенной цепи). На рис. 7.7 показано, как настроить мультиметр для проверки целостности цепи, включая гнезда для подключения измерительных проводов. Обратите внимание, что на фотографии я просто соприкоснул щупы, чтобы продемонстрировать, как проверить целостность. Мне нравится делать это просто для того, чтобы убедиться, что мой мультиметр включен и имеет правильные настройки1. 1 Да, это один из конструкторских приемов. Проверка, проверка и еще раз проверка. – Прим. авт.
234  Электроника для начинающих Рис. 7.7  Настройки для проверки целостности цепи («прозвонки») Еще одно отличное применение режима «прозвонки» – диагностика или выяснение правильности подключения кабелей. Например, вы можете использовать «прозвонку», чтобы определить, какой контакт разъема подключен на каждом конце кабеля (в старые времена это делалось с помощью телефонного звонка и источника тока для него, отсюда «прозвонка»). Измерение напряжения Наши IoT-проекты используют питание постоянным током (DC). Поэтому для измерения напряжения в цепи воспользуемся диапазоном DC мультиметра (помеченным буквой V с прямой и пунктирной линиями сверху). Обратите внимание, что диапазон напряжений имеет несколько положений для выбора масштаба. Выберите шкалу, которая соответствует диапазону напряжения, который вы хотите протестировать1. Например, в наших IoT-проектах мы часто измеряем напряжение в пределах 3,3–12 В, поэтому выбираем шкалу 20 на циферблате. Затем подключите черный измерительный провод к COMгнезду, а красный измерительный провод к гнезду с маркировкой VΩHz. Теперь нам нужно что-то измерить! Возьмите любую батарейку, которая есть у вас дома, и прикоснитесь черным щупом к отрицательному контакту, а красным щупом к положительному. На дисплее должно появиться значение напряжения, близкое к значению для заряженной батарейки. Например, если 1 Ничего страшного не произойдет, если вы перепутаете диапазоны – при установке диапазона 200 В мультиметр покажет меньшее число знаков после запятой, а при установке 2 В покажет выход за пределы шкалы. Если же вы перепутаете шкалы для постоянного и переменного напряжений, мультиметр просто покажет нулевое значение. – Прим. перев.
Основы  235 вы имеете пальчиковую батарейку, то должны увидеть напряжение около 1,5–1,6 В. Оно может быть меньше, если батарея разряжена. Итак, теперь вы знаете, как проверить батарейки на свежесть1! На рис. 7.8 показано, как измерить напряжение батарейки. Рис. 7.8  Измерение напряжения батарейки Обратите внимание, что на дисплее отображается значение 1,50, что соответствует правильному напряжению для этой батарейки типа АА 2. Если бы я поменял местами щупы – красный к отрицательному выводу, а черный к положительному, – на дисплее было бы показание –1,50. Это нормально, поскольку показывает, что ток течет в направлении, противоположном ориентации щупов. ПРИМЕЧАНИЕ Если при измерении напряжения в цепи постоянного тока вы неправильно ориентируете щупы, мультиметры будут отображать напряжение в виде отрицательного числа. Попробуйте это со своей батареей. Мультиметру (или аккумулятору) это не повредит! Мы можем использовать эту технику для измерения напряжения в наших проектах. Просто будьте осторожны, размещая щупы в соответствующих положениях, и старайтесь не перекрещивать и не закорачивать с другими контактами, касаясь наконечником щупа более чем одного контакта одновременно. 1 2 У аккумуляторов, в отличие от одноразовых батареек, в зависимости от их типа напряжение при полном заряде может отличаться, даже если типоразмеры совпадают. Например, у пальчиковых аккумуляторов NiMh нормальное напряжение 1,3–1,4 В, тогда как для щелочных батареек эти значения свидетельствуют о су­ щественном разряде. – Прим. перев. Если быть точным, напряжение 1,50 В ровно свидетельствует о том, что в некоторой (небольшой) степени батарейка использована (или долго хранилась). Начальное напряжение для свежих щелочных батареек – около 1,61–1,62 В. – Прим. перев.
236  Электроника для начинающих Измерение тока Ток измеряется в единицах силы тока (амперах – A или миллиамперах – мА). Мы будем использовать диапазон DC, отмеченный буквой А с прямой и пунк­ тирной линией (а не волнистой – это диапазон АС). Ток измеряется последовательным подключением в цепи, то есть мы должны включить мультиметр в схему. Это может быть немного сложнее, потому что мы должны прервать поток тока и подключить в разрыв мультиметр. Если вы умеете пользоваться макетной платой, то можете продолжить этот эксперимент. Однако если вы еще не использовали макетную плату, то можете прочитать об этом эксперименте, а затем вернуться к нему по окончании данной главы. Для этого эксперимента мы будем использовать макетный блок питания, светодиод и резистор. Цепь соединим таким образом, чтобы использовать мультиметр для ее завершения. На рис. 7.9 показано, как настроить схему со встроенным в нее мультиметром. Черный щуп здесь Красный щуп здесь Рис. 7.9  Измерение тока Перед включением источника питания подключите черный щуп к COMпорту, а красный – к порту с маркировкой мА. Некоторые мультиметры используют один и тот же порт для измерения напряжения и тока1. Установите переключатель мультиметра в положение 200 мА. Затем включите пита1 Но это редкость. Единственный способ всерьез повредить мультиметр – подключить его в режиме измерения тока (через соответствующие гнезда, а не просто переключателем на шкале) к достаточно мощному источнику напряжения (источнику питания, батарейке или электрической сети). Если это просто будет сгоревший предохранитель, о котором упоминает автор далее, ты вы легко отделались. Именно поэтому гнезда для измерения токов в мультиметре почти всегда ставят отдельно от гнезд для остальных режимов. – Прим. перев.
Основы  237 ние макета и прикоснитесь щупами к указанным местам. Будьте осторожны и прикасайтесь только к контакту VCC на блоке питания макетной платы. Как только источник питания будет включен, вы должны увидеть значение на мультиметре. На рис. 7.10 показано, как использовать мультиметр для измерения тока в цепи. Рис. 7.10  Измерение тока Есть еще одна сложная вещь в измерении тока. Если вы попытаетесь измерить ток, превышающий максимальный для гнезда, то можете повредить мультиметр (например, мультиметр на фотографии имеет максимум 20 мА на данном гнезде). Если бы я превысил это значение, скажем, включив в цепь с током 5 А, скорее всего, перегорел бы предохранитель в мультиметре. Это нежелательно, но по крайней мере есть предохранитель, который мы можем заменить, если допустим ошибку и выберем неправильный диапазон. Измерение сопротивления Сопротивление измеряется в омах (ohm, Ом, Ω). Наиболее распространенным компонентом, который мы будем использовать для введения сопротивления в цепь, является резистор. Мы можем проверить сопротивление резистора с помощью нашего мультиметра. Для проверки выберите диапазон шкалы Ом (Ω), наиболее близкий к номиналу резистора. Например, я собираюсь протестировать резистор, сопротивление которого, по моему мнению, составляет около 200 Ом, но поскольку я не уверен, то выберу диапазон 2 кОм. Затем подключите черный тестовый щуп к COM-гнезду, а красный щуп к гнезду с маркировкой VΩHz. Теперь прикоснитесь одним щупом к одному выводу резистора, другим – к другому выводу. Не важно, вывод с какой стороны вы выберете, – резистор работает в обоих направлениях одинаково (иначе говоря, не имеет полярности). Обратите внимание на показания: мультиметр показывает одно из трех значений: 0,00 (короткое замыкание),
238  Электроника для начинающих 1 (недостаточный диапазон или отсутствие контакта) или фактическое значение резистора. В данном случае мультиметр показывает 0,219, что означает, что сопротивление этого резистора составляет 219 Ом (0,219 кОм). Напомним, я использовал шкалу 2 кОм, что означает, что резистор номиналом ровно 1 кОм будет показывать 1,000. Поскольку значение является десятичным, я могу переместить десятичную точку влево, чтобы получить целое число ом. Если мультиметр отображает значение 1 (без десятых), это указывает на то, что диапазон выбран неправильно и вам следует попробовать его увеличить. Это не проблема. Это просто означает, что вам нужно выбрать другой масштаб. Если на дисплее отображается 0 или очень маленькое число, необходимо выбрать меньшее значение диапазона. Поворачивать ручку переключателя можно в любом направлении, когда проверяется сопротивление в неизвестной цепи. На рис. 7.11 показан пример измерения сопротивления резистора. Обратите внимание, что на дисплее отображается 219. Я проверяю резистор номиналом 220 Ом. Причина, по которой значение показаний равно 219 вместо 220, заключается в том, что резистор, который я использую, рассчитан на допуск 220 ± 5%. Таким образом, приемлемый диапазон для этого резистора составляет 209–231 Ом. Рис. 7.11  Измерение сопротивления резистора Теперь мы знаем, как проверить резистор, чтобы узнать его номинал. Как мы увидим, цветные кольца на корпусе резистора – это основной способ узнать его номинал, но мы всегда можем проверить его мультиметром, если кто-то его закрасил или слишком лень искать таблицу расшифровки цветных кодов. Сейчас давайте обсудим самую фундаментальную концепцию, которую вы должны понимать при работе с электроникой, – питание вашего проекта!
Основы  239 Питание электроники Электричество1 кратко определяется как поток электрического заряда2, и при его использовании обеспечивается питание нашей электроники – от обычной лампочки или потолочного вентилятора до телевизора высокой четкости или нового планшета. Независимо от того, питаете ли вы свою электронику от батарей или источника питания, вы запускаете цепь, в которой электроны движутся по определенным схемам. Есть две формы (или разновидности) электрического тока, которые мы используем. Ваш дом питается от переменного тока, а ваша электроника – от постоянного тока. Термин переменный ток (AC) используется для описания потока заряженных частиц, который периодически меняет направление с определенной частотой (или периодом), меняя знак напряжения вместе с током. Таким образом, системы переменного тока предназначены для работы в определенном диапазоне частот и значений напряжения. Обычно в системах переменного тока используется более высокое напряжение, чем в системах постоянного тока. Термин постоянный ток (DC) используется для описания потока заряженных частиц, которые не меняют направление и, следовательно, всегда текут в определенном «направлении». Большинство электронных систем питаются постоянным напряжением и обычно имеют более низкое напряжение, чем системы переменного тока. Например, проекты интернета вещей обычно работают при напряжении постоянного тока (DC) в диапазоне 3,3–24 В. СОВЕТ Для получения дополнительной информации о переменном и постоянном токе и различиях см. https://learn.sparkfun.com/tutorials/alternating-current-ac-vs-directcurrent-dc3. Поскольку постоянный ток течет в одном направлении, компоненты, работающие от постоянного тока, имеют положительные и отрицательные выво1 2 3 https://learn.sparkfun.com/tutorials/what-is-electricity. – Прим. авт. То же самое на русском языке: https://ru.wikipedia.org/wiki/Электричество. – Прим. перев. Ошибочное определение: поток электрического заряда определяет только один из феноменов явления электричества, а именно электрический ток. Кроме элект­ рического тока (динамического, текущего электричества, могущего совершать полезную работу), немаловажная составляющая понятия электричества – статическое электричество, накопление неподвижных зарядов. Оно не только вредит электронным схемам, как об этом упоминалось раньше, – важность понятия статического электричества вытекает хотя бы из того, что оно стало известно гораздо раньше динамического (электростатическая машина изобретена еще в XVII веке, а само явление известно с древних времен). И что еще существеннее для нас – на нем основываются понятие конденсатора и один из базовых законов электричест­ ва – закон Кулона, служащий основой для всех остальных. – Прим. перев. То же на русском языке: https://dzen.ru/a/YonHePxFI3eJGCBh. – Прим. перев.
240  Электроника для начинающих ды, где ток течет от положительного вывода к отрицательному1. Ориентация этих сторон – одна на положительную, другая на отрицательную – называется полярностью. Некоторые компоненты, такие как резисторы, могут работать в любом направлении, но полярные компоненты вы всегда должны подключать с соблюдением полярности. Большинство таких компонентов имеют четкие обозначения, а те, которые не имеют обозначения, имеют известное расположение. Например, положительный полюс светодиода, называемый анодом, – это более длинный из двух выводов, тогда как отрицательный или более короткий вывод называется катодом2. Несмотря на относительно низкие напряжения, не следует думать, что они совершенно безвредны или безопасны. Неправильное подключение электроники (изменение полярности) или короткое замыкание (соединение плюса и минуса вместе) может привести к повреждению электронной схемы и в некоторых случаях вызвать перегрев вплоть до возгорания устройства. ВНИМАНИЕ! Не поддавайтесь искушению думать, что работа с напряжением 3,3 или 5,5 В «безопасна». Даже короткое неправильное подключение напряжения может привести к необратимо разрушительным результатам. Не думайте, что низкое напряжение постоянного тока безвредно для электронных схем. Пару лет назад я получил урок того, насколько реальным может быть этот сценарий. Я менял батарейки в домашних детекторах дыма. Я вынул старые батарейки и положил их в карман. Я забыл, что у меня в том же кармане лежит маленький перочинный ножик. Нож закоротил одну из батарей, и примерно через десять минут она нагрелась до неприятно высокой температуры. Этого было недостаточно, чтобы сжечь мои брюки и нанести ожоги, но если бы я оставил что-то подобное без присмотра, все могло бы быть гораздо хуже. Это пугающая мысль, не так ли? Считайте это не только советом, но и предостережением; мы никогда не должны игнорировать методы безопасного обращения, даже в проектах с низким напряжением. 1 2 Обратите внимание, что условие протекания электрического тока – наличие замкнутого проводящего контура в цепи. В этом контуре ток движется в одном направлении. Отсюда вытекает следующее: если мы принимаем, что у потребителя электрической энергии (электронной схемы, двигателя, светодиода) ток течет от положительного полюса к отрицательному, то, следовательно, у источника направление тока противоположное: ток внутри батарейки или аккумулятора течет от отрицательного полюса к положительному. Картину дополнительно запутывает факт, что собственно носители заряда в металлах (электроны) заряжены отрицательно и физически перемещаются в направлении, противоположном току (отрицательные заряды притягиваются к положительному полюсу). Объясняется такая путаница просто: Кулон и его последователи вводили понятия о «положительном» и «отрицательном» полюсах и, следовательно, о «прямом» направлении тока произвольно, когда про электроны еще ничего не было известно. – Прим. перев. Заметим, что правило «длинный вывод – положительный» действует также и, например, для таких распространенных компонентов, как электролитические конденсаторы. А вот для обычных (несветоизлучающих) диодов это правило не применяют (см. далее). – Прим. перев.
Электронные компоненты  241 Наконец, компоненты постоянного тока часто рассчитаны на определенный диапазон напряжений. Вспомните наше обсуждение различных недорогих вычислительных плат и разъемов GPIO: некоторые платы работают при напряжении 5 В, тогда как другие – при 3,3 В (или даже меньше). К счастью, есть несколько способов адаптировать устройства, работающие при разном напряжении, если использовать дополнительные компоненты. ПРИМЕЧАНИЕ Я намеренно сделал обсуждение питания простым. Электрический ток (даже только постоянный) – это гораздо больше, чем то, что я здесь описал. Поняв эти основы, вы сможете работать с проектами, описанными в данной книге. Электронные компоненты Помимо изучения того, как пользоваться мультиметром и, возможно, научиться паять, вам также необходимо кое-что знать об электронных компонентах, доступных для создания проектов. В этом разделе я привожу краткий список и описание некоторых распространенных компонентов в алфавитном порядке по названиям, с которыми вы столкнетесь при создании решений интернета вещей. Я также рассказываю о внешних модулях, которые представляют собой небольшие схемы, построенные из набора компонентов, обеспечивающих некую функциональность. Например, вы можете приобрести платы для подключения USB-хостов, модули Ethernet, логические переключатели, таймеры и многое другое. Кнопки Кнопка (иногда называемая кнопкой мгновенного действия) – это механизм, который устанавливает соединение при нажатии. Точнее, кнопка соединяет два или более контакта вместе, пока она нажата. Распространенный (и, возможно, всем знакомый) пример – кнопка домашнего дверного звонка. При нажатии она замыкает цепь, которая запускает звуковой сигнал: звонок, тон или музыку. Некоторые дверные звонки продолжают звучать, только пока нажата кнопка. В IoT-проектах мы будем использовать кнопки для запуска событий, запус­ ка и остановки действий и подобных операций. Кнопка – это самая простая форма выключателя, но, в отличие от выключателя, для поддержания элект­ рического соединения необходимо продолжать нажимать кнопку. Большинство кнопок имеют как минимум две ножки (вывода), которые соединяются при нажатии кнопки. Некоторые из них имеют более двух направлений, соединенных попарно, а иные допускают несколько соединений. На рис. 7.12 показано несколько кнопок разных конструкций. Существует специальный вариант кнопки, называемый кнопкой с фиксацией. В этой версии используется фиксатор, чтобы удерживать контакты
242  Электроника для начинающих в соединении до тех пор, пока кнопку не нажмут снова. Если вы видели кнопку на стереосистеме или в своей машине, которая остается нажатой до тех пор, пока ее не нажмут снова, скорее всего, это кнопка с фиксацией. Рис. 7.12  Кнопки Существуют всевозможные кнопки: от тех, которые можно использовать с макетными платами (расстояние между контактами позволяет вставлять их в гнезда макетной платы1), которые можно монтировать на панель, до тех, которые предназначены для пайки на печатных платах. Конденсаторы Конденсатор предназначен для хранения зарядов. Когда ток протекает через конденсатор, он накапливает заряд и может разряжаться после отключения тока. В этом смысле он похож на аккумулятор, но в отличие от последнего конденсатор заряжается и разряжается очень быстро. Мы используем конденсаторы для хранения заряда, для блокировки переменной, составляющей напряжения, снижения шума в источниках питания и аудиосхемах и т. д. На рис. 7.13 показано несколько разновидностей конденсаторов. Существует несколько типов конденсаторов, но в IoT-проектах чаще всего мы сталкиваемся с конденсаторами при создании источников питания. Большинство конденсаторов для этой цели – полярные и имеют две ножки (вывода), один из которых (длинный) положительный, а другой (короткий) – отрицательный и обычно дополнительно помечен толстым минусом на корпусе. Обязательно подключайте конденсатор к схеме с соблюдением правильной полярности. 1 Подобная кнопка показана на рис. 7.12 в центре – такие разновидности еще называют «тактовыми кнопками». – Прим. перев.
Электронные компоненты  243 Рис. 7.13  Конденсаторы Диоды Диод устроен так, что пропускает ток только в одном направлении. Большинство из них отмечены кольцом на одном конце корпуса, указывающим на отрицательный вывод (катод), иногда условной стрелкой по направлению тока. Диод часто используется совместно с другими компонентами в качестве выпрямителей в преобразователях переменного тока в постоянный (точнее, переменного напряжения в постоянное), для подавления скачков напряжения или защиты от обратного напряжения при неправильном включении. Их часто используют для защиты от тока, протекающего в непредусмотренном направлении1. Большинство диодов имеют форму небольшого цилиндра, обычно черного цвета с серебряной надписью и кольцом, обозначающим катод, и два вывода. Они немного похожи на резисторы. В источниках питания иногда используется специальный вариант диодов, называемый стабилитронами, которые помогают поддерживать определенное напряжение. На рис. 7.14 показаны типичные диоды. Рис. 7.14  Диоды 1 Автор имеет в виду частый случай организации двойного питания, как, например, плат контроллеров при подключении к USB и к внешнему источнику. В этом случае одно из питаний (обычно USB) должно автоматически отключаться, чтобы не вызвать замыкания источников друг на друга, – для этого их «развязывают» диодами, установленными в определенном направлении. – Прим. перев.
244  Электроника для начинающих Предохранители Предохранитель предназначен для защиты устройства (всей цепи) от тока, превышающего тот, который могут безопасно эксплуатировать компоненты или источник питания. Предохранители располагаются на положительном выводе схемы. Когда через предохранитель протекает слишком большой ток, это вызывает его срабатывание, обеспечивая прерывание тока. В некоторых предохранителях используется специальный провод, который плавится или ломается (тем самым выводя предохранитель из строя, но защищая ваше оборудование), в то время как другие предохранители используют механизм, который работает как выключатель (многие из них имеют возможность перезапуска). Когда это происходит, мы говорим, что предохранитель «перегорел» или «сработал». Предохранители рассчитаны на определенный ток в амперах, что указывает на максимальный ток, который предохранитель пропускает без срабатывания. Предохранители бывают разных форм и разновидностей и могут работать с переменным или постоянным напряжением. Те, которые мы будем использовать, относятся к одноразовым. На рис. 7.15 показан пример двух разновидностей: ножевого предохранителя автомобильного типа слева и предохранителя со стеклянным корпусом справа. Рис. 7.15  Предохранители Если вы знакомы с электрическим щитком в вашем доме, на котором установлены автоматические выключатели, то это пример самовосстанавливающихся предохранителей. В следующий раз, когда один из них издаст щелчок и свет погаснет, вы можете сказать: «Эй, предохранитель сработал!» Более того, теперь вы знаете, почему – вы превысили максимальный ток автоматического выключателя. Это, вероятно, нормально в ситуациях, если вы случайно оставили электрообогреватель включенным, решив поджарить тосты в электродуховке (такое случается), но если автоматы часто отключаются без какой-либо нагрузки, вам следует позвонить электрику, чтобы проверить проводку.
Электронные компоненты  245 Светодиоды (LED) Как мы узнали в главе 3, у светодиода есть два вывода, более длинный из которых является положительным, а более короткий – отрицательным1. Светодиоды также имеют плоский край, обозначающий отрицательную часть. Они бывают разных размеров: от 3 до 10 мм в диаметре. На рис. 7.16 показан пример светодиодов размеров 3 и 5 мм. Рис. 7.16  Светодиоды Напомним, что со светодиодом также нужно использовать резистор. Это необходимо, чтобы ограничить ток, протекающий через светодиод, до допус­ тимого значения. Светодиоды можно использовать с меньшим током (они будут гореть немного тусклее, чем обычно), но не следует использовать их с более высоким током, чем предельно допустимое значение. Чтобы определить, какой номинал резистора нужен, следует знать несколько параметров. Эти данные можно получить у производителя, который предоставляет данные в виде таблицы данных в описании или иногда приводит их на упаковке. Необходимые нам данные включают прямое падение напряжения на светодиоде, номинальный ток светодиода, и еще нам необходимо знать напряжение питания. 1 У светодиодов в круглых корпусах (как на рис. 7.16) дополнительно отрицательный вывод (катод) обозначается скосом на бортике корпуса. – Прим. перев.
246  Электроника для начинающих Например, если у меня есть светодиод, подобный тому, который мы использовали в предыдущей главе, в данном случае красный светодиод диамет­ ром 5 мм, на веб-сайте поставщика (https://www.sparkfun.com/products/9590) мы обнаружим, что светодиод работает при прямом напряжении 1,8–2,2 В и номинальном токе 20 мА. Допустим, мы хотим использовать его с напряжением питания 5 В. Мы можем взять эти значения и подставить их в формулу1: R = (Vcc – Vf )/I. Используя более описательные имена для переменных, мы получаем следующее: Сопротивление (ом) = (Питание (вольт) – Падение (вольт)) / Ток (ампер). Подставив наши данные, мы получим результат, показанный ниже. Обратите внимание, что у нас ток задан в миллиамперах, поэтому мы должны делить его на 1000 (чтобы получить амперы). В данном случае это 0,020, а падение напряжения мы выберем среднее (2 В): Сопротивление = (5 – 2.0)/0.020 = 3.0/0.020 = 150 Ом. Таким образом, нам понадобится резистор сопротивлением 150 Ом2. Отлично! Иногда формула дает значение, которое не соответствует ни одному из существующих номиналов резисторов. В этом случае выберите тот, который ближе всего к значению, но немного больше. Помните, мы хотим ограничить и поэтому ошибаемся в сторону более строгих ограничений. Например, если вы обнаружили, что вам нужен резистор сопротивлением 95 Ом, вы можете использовать резистор номиналом 100 Ом, что безопаснее, чем использование резистора номиналом 91 Ом. СОВЕТ Всегда выбирайте более ограничительный резистор (большего номинала), если формула дает значение, для которого нет подходящего стандартного значения. 1 2 Вариант закона Ома (https://en.wikipedia.org/wiki/Ohm’s_law). – Прим. авт. То же порусски: https://ru.wikipedia.org/wiki/Закон_Ома. – Прим. перев. Сигнальные светодиоды в круглых корпусах нормально (без перегрузки) светятся при токе от 3 до 15 мА, т. е. для красных-зеленых-желтых разновидностей (падение напряжения 1,5–2,5 В) при питании 5 В токоограничивающий резистор может быть номиналом от 200 Ом до 1 кОм, для синих (падение напряжения 3–3,5 В) – от 120 до 620 Ом. При подключении к выходу контроллеров ситуация сложнее: надо учитывать также выходное напряжение и нагрузочную способность порта контроллера, к которому подключается светодиод: так, для Nano RP2040 Connect, по сведениям официального сайта Arduino.cc, максимальный ток через I/O-вывод составляет 12 мА (для Pico официальных данных найти не удалось, но, так как контроллер тот же, можно принять на том же уровне). Следовательно, к портам этих контроллеров, работающих с напряжением 3,3 В, красные-зеленые-желтые светодиоды следует подключать с резистором около 100–150 Ом (синие при таком напряжении могут не работать вовсе). Для суперъярких разновидностей мелких светодиодов в SMD-корпусах (подобно установленным на микроконтроллерных платах) рабочий ток может быть значительно меньше, потому для них сопротивление установленных на плате резисторов 1 кОм – нормальное рабочее значение при любом напряжении. – Прим. перев.
Электронные компоненты  247 Кроме того, если вы используете светодиоды последовательно или параллельно1, формула немного другая. См. https://learn.adafruit.com/all-about-leds для получения дополнительной информации об использовании светодиодов в ваших проектах и о расчете номинала резисторов для использования со светодиодами. Реле Реле – интересный компонент, который помогает нам переключать более высокие напряжения с помощью цепей с более низким напряжением. Например, предположим, что вы хотите управлять устройством, питающимся от напряжения 12 В от платы MicroPython, которая выдает максимум 3,3 В. Реле можно использовать с трехвольтовым выходом контроллера для включения питания от этого источника с более высоким напряжением. В этом примере мы будем использовать выход платы MicroPython, чтобы активировать реле для включения питания 12 В. Таким образом, реле представляют собой разновидность выключателя или переключателя. На рис. 7.17 показано типичное реле (расположение контактов указано на корпусе слева). Рис. 7.17  Реле Реле могут иметь множество различных конфигураций и, как правило, имеют различающиеся варианты подключения, например для подачи напряжения питания и использования контактов переключателя. Различаются они также начальным состоянием контактов, управляющих внешней нагрузкой: разомкнутым (нет тока) или замкнутым (есть ток). Некоторые реле постав1 При последовательном включении просто суммируются падения напряжения на каждом из светодиодов. А параллельно светодиоды (как и диоды вообще) включать бессмысленно, т. к. результат непредсказуем: вплоть до того, что один из пары не будет светиться вовсе, особенно если они разного цвета. Но даже если светодиоды одинаковые и из одной серии, все равно один из пары всегда будет светиться туск­ лее другого из-за технологического разброса параметров. Кстати, по указанной автором ссылке размещено очень толковое и подробное пособие по подключению светодиодов (на английском языке), но ни последовательное, ни тем более параллельное их включение там не упоминается. – Прим. перев.
248  Электроника для начинающих ляются на печатной плате с четко обозначенными клеммами для функций переключения и местами подключения к управляющей цепи. Если вы хотите использовать реле в своих проектах, всегда проверяйте таблицу данных в описании, чтобы убедиться, что вы его правильно подключаете. Вы также можете использовать реле, чтобы ваша цепь постоянного тока могла включать и выключать приборы переменного тока с сетевым напряжением, например такое от Adafruit (www.adafruit.com/product/2935). Резисторы Резистор является одним из стандартных строительных блоков электроники. Его задача – ограничивать ток и снижать напряжение (которое преобразуется в тепло). Этот эффект, известный как электрическое сопротивление, как мы уже указывали в прошлой главе, измеряется в омах (ohm, Ом, Ω). Резис­ тор можно использовать для снижения напряжения на других компонентах, ограничения частотной характеристики или для защиты чувствительных компонентов от перенапряжения. На рис. 7.18 показано несколько резисторов с гибкими выводами1 (в заводской упаковке). Рис. 7.18  Резисторы Резистор является неполярным компонентом и может подключаться в любом направлении. Он может использоваться для фиксации напряжения на контакте схемы: путем подключения второго конца резистора к положительному напряжению (подтягивающий резистор) или к общему проводу (заземляющий резистор), что исключает возможность нахождения напряжения на контакте в неопределенном состоянии. Подтягивающий резистор 1 Кроме резисторов с гибкими выводами (тип монтажа through hole, см. далее), которые удобно использовать для макетирования, широко применяются резисторы для поверхностного монтажа (SMD-резисторы). Они занимают значительно меньше места, и их легко найти на любой плате с контроллерами или модуле с датчиками. – Прим. перев.
Электронные компоненты  249 гарантирует, что на контакте напряжение имеет положительное значение, а заземляющий резистор – что на контакте имеется нулевое напряжение (общий провод). Переключатели Переключатели (тумблеры) предназначены для управления протеканием тока между двумя или более контактами. Переключатели бывают самых разных форм, размеров и конструкций. Некоторые из них предназначены для простого включения/выключения (как обычные выключатели), в то время как другие можно использовать для переключения направления тока с одного набора контактов на другой. Как и кнопки, переключатели имеют различные варианты монтажа: от печатной платы (также называемой монтажом в сквозные отверстия, through hole) до крепления на панели для установки в корпуса. На рис. 7.19 показаны различные тумблеры. Рис. 7.19  Различные переключатели (тумблеры) Переключатели, имеющие только один полюс (входной контакт), называются однополюсными. Переключатели, которые могут перенаправлять ток от одного набора полюсов к другому, называются двухполюсными переключателями. Переключатели, у которых на полюс имеется только одно соединение, называются однопозиционными. Переключатели, которые отключаются от одного набора выходов и подключаются к другому, сохраняя при этом общий вход, называются двухпозиционными переключателями. Их часто объединяют и образуют тип (или разновидность) переключателя следующим образом:   SPST (Single Pole, Single Throw): один полюс, одно направление (обычный выключатель);
250  Электроника для начинающих   SPDT (Single Pole, Double Throw): один полюс, два направления (перекидной контакт с одного входа на тот или иной выход);   DPST (Double Pole, Single Throw): два полюса, одно направление (эквивалент двух обычных выключателей SPST, переключаемых совместно);   DPDT (Double Pole, Double Throw): два полюса, два направления (эквивалент двух переключателей SPDT, переключаемых совместно);   3PDT: три полюса, два направления и т. д. Могут быть и другие варианты, с которыми вы можете столкнуться. Мне нравится представлять это следующим образом: если у меня есть только ситуация включения/выключения, мне нужен один выключатель. Количест­ во полюсов зависит от того, сколько проводов или цепей я хочу включить или выключить одновременно. Двухпозиционные переключатели я использую, когда у меня есть состояние «A» и состояние «B», когда я хочу, чтобы A включался, когда B выключен, и наоборот. Иногда я использую многопозиционные переключатели, когда мне нужны состояния «A», «B» и ситуация выключения обоих, тогда я использую центральное положение. Вы можете быть очень креативны с переключателями! Транзисторы Транзистор (биполярный транзистор) предназначен для периодического включения/выключения тока или усиления колебаний тока. Интересно, что транзисторы, используемые для усиления тока, заменили электронные лампы. Если вы аудиофил, то, вероятно, многое знаете о электронных лампах. Когда транзистор работает в режиме включения/выключения, он ведет себя подобно реле, но его положение «выключено» по-прежнему позволяет протекать небольшому количеству тока. Транзисторы используются в аудиоаппаратуре, обработке сигналов и импульсных источниках питания. На рис. 7.20 показаны две разновидности маломощных транзисторов. Рис. 7.20  Транзисторы Транзисторы бывают самых разных типов, в разнообразных корпусах и с широким диапазоном номинальных параметров, которые делают их подходящими для того или иного решения.
Электронные компоненты  251 Стабилизаторы напряжения Стабилизатор напряжения (линейный стабилизатор напряжения) предназначен для поддержания постоянного уровня напряжения (обычно питающего) в электронной схеме. Стабилизаторы напряжения часто употребляются в электронике, когда нужно стабилизировать или снизить напряжение от источника. Например, нам нужно подать в цепь напряжение 5 В, но у нас есть источник питания только с напряжением 9 В. Стабилизаторы напряжения выполняют это, ограничивая напряжение на заданном уровне и рассеивая избыток в виде тепла через корпус или радиатор. Таким образом, стабилизаторы напряжения имеют три вывода: вход положительного напряжения источника, положительное напряжение на выходе и общий (отрицательный)1. Обычно они имеют форму, показанную на рис. 7.21, но существуют и другие разновидности. Рис. 7.21  Регуляторы напряжения Небольшое отверстие в пластине, выходящей за пределы корпуса стабилизатора напряжения, предназначено для установки на радиатор, рассеивающий тепло. Регуляторы напряжения часто обозначаются в соответствии с их номинальным напряжением выхода. Например, LM7805 выдает 5 В, а LM7833 – 3,3 В. Пример использования стабилизатора напряжения для подачи питания в цепь 3,3 В на макетной плате показан на рис. 7.22. Эта схема содержит конденсаторы, которые помогают сглаживать или регулировать мощность2. Обратите внимание, что конденсаторы имеют номинал мкФ, что означает микрофарад. 1 2 Существуют и аналогичные стабилизаторы отрицательного напряжения (относительно общего провода). – Прим. перев. Входной конденсатор 1 мкФ имеет смысл выбирать керамический (на рис. 7.13 такой показан посередине, желтого цвета) – он неполярный, меньше по габаритам и к тому же лучше отсеивает высокочастотный паразитный сигнал на входе. – Прим. перев.
252  Электроника для начинающих Рис. 7.22  Схема блока питания на макетной плате со стабилизатором напряжения Внешние модули и схемы О внешних (подключаемых) модулях уже шла речь в главе 6 – это наши модульные строительные блоки для решений интернета вещей. Обычно они объединяют несколько компонентов вместе, чтобы сформировать такую функцию, как измерение температуры, считывание данных GPS, связь через услуги сотовой связи и многое другое. На рис. 7.23 показаны два внешних модуля. Обратите внимание на отверстия для подключения платы к плате MicroPython через макетную плату. Рис. 7.23  Внешние модули (с разрешения adafruit.com) Слева – аналого-цифровой преобразователь (www.adafruit.com/product/1083), справа – датчик барометрического давления (www.adafruit.com/ product/1893).
Использование макетной платы для создания схем  253 Всякий раз, когда вы разрабатываете схему или решение интернета вещей, стоит как можно чаще использовать внешние модули, поскольку они упрощают использование компонентов. Возьмем, к примеру, датчик барометрического давления. Adafruit разработала эту плату так, что все, что нам нужно сделать для ее использования, – это подключить питание и подключить ее к нашему IoT-устройству по шине I2C. Шина I2C, как мы знаем, – быстрый цифровой протокол, который использует два провода (плюс питание и общий) для чтения данных из схем или устройств. Таким образом, вам не нужно беспокоиться о том, как подключить датчик к другим компонентам для его использования – просто подключите его, как любое I2C-устройство, и начинайте считывать данные! Мы будем использовать некоторые из таких плат в проектах далее в этой книге. Использование макетной платы для создания схем Если вы до сих пор следили за проектами в книге, вы уже встречали макетную плату для создания простых схем. Вспомните из главы 3, что макетная плата – это инструмент, который мы используем для подключения компонентов при объединении их в схему. Мы здесь используем беспаечную макетную плату. Макетная печатная плата под пайку имеет примерно такую же компоновку, за исключением того, что на печатной плате имеются только площадки для пайки со сквозными отверстиями. Почему макетные платы по-английски называют breadboard1? В великие старые времена микроэлектроника и дискретные компоненты стали широко доступны для экспериментов, и когда хотелось создать прототип схемы, некоторые использовали кусок дерева со вбитыми в него на равных расстояниях гвоздями, а соединения осуществлялись оборотом провода вокруг гвоздей (так называемые «соединения накруткой»). Некоторые использовали кухонные разделочные доски (т. е. собственно «breadboard») для создания прототипов с помощью проволочной накрутки. С тех пор это название закрепилось. Макетная плата позволяет создавать прототипы схем или просто временные схемы без необходимости тратить время (и деньги) на изготовление печатной платы. Прототипирование – это процесс экспериментирования со схемой и тестирования ваших идей. Фактически как только наша схема будет работать правильно, мы сможем использовать макет на макетной плате 1 В буквальном переводе «хлебная доска», «доска для хлеба». – Прим. перев.
254  Электроника для начинающих как образец для проектирования печатной платы1. На рис. 7.24 показано несколько макетных плат. Рис. 7.24  Различные макетные платы Напомним, что большинство макетных плат (их существует несколько разновидностей) имеют центральную канавку или напечатанную линию по центру платы. Это означает, что клеммные гнезда, идущие перпендикулярно каналу, не соединены между собой. Это позволяет подключать интегральные схемы (ИС, микросхемы) или модули (подключаемые платы), контакты на которых расположены в виде двух рядов. Таким образом, мы можем подклю1 Вообще говоря, смешивать понятия «прототипирование» и «макетирование» не следует. Создание прототипа имеет целью построить грубую реализацию основной идеи устройства, прототип может не содержать существенных компонентов, необходимых для функционирования в рабочем режиме. Поэтому образцом для создания окончательной печатной платы прототип служить не может – для этой цели по наработкам, полученным в процессе прототипирования, строят именно макет, то есть полностью функционирующее устройство, но, может быть, без корпуса и с временными соединениями между компонентами. Другое дело, что в любительской практике прототипирование и макетирование чаще всего представляют один и тот же этап, отчего американские авторы эти понятия часто смешивают. В российской литературе (ориентированной на непрофессионалов) в аналогичных случаях принято всегда употреблять более точный термин «макетирование». – Прим. перев.
Использование макетной платы для создания схем  255 чить микросхему к макетной плате с помощью набора контактов на каждой стороне. Мы увидим это на примере далее. Большинство макетов также имеют один или несколько комплектов шин питания, которые соединены вместе параллельно центральной линии. Если есть два набора, они не соединяются друг с другом. Шины питания могут маркироваться цветными линиями (обычно синей и красной), но это только для справки; вы любую из них можете соединить с положительным выводом питания, а другую – с отрицательным. Наконец, на некоторых макетах ряды клеммных колодок нумеруются. Они также предназначены только для справки и не имеют никакого другого значения. Однако они могут быть полезны для заметок в вашем инженерном ежедневнике. На рис. 7.25 показана типовая макетная плата и имеющиеся соединения клеммных гнезд между собой в рядах и на шинах питания. Шины питания Центральная разделяющая канавка Клеммные гнезда Шины питания Рис. 7.25  Типовая макетная плата ПРИМЕЧАНИЕ Наборы шин питания на разных сторонах не соединены между собой. Если вы хотите, чтобы питание было подано на обе стороны макетной платы, для их соединения необходимо использовать перемычки. Fritzing: программное приложение для макетирования Чертежи макетов в этой книге были сделаны с помощью программы Fritzing (https:// fritzing.org/). Это недорогое приложение позволяет создать цифровое представление схемы на макетной плате. Это довольно удобно в использовании. Если вы захотите разработать прототип схемы, использование Fritzing поможет вам избежать многих проб и ошибок. В качестве бонуса Fritzing позволяет вам увидеть разработанный проект в виде электронной схемы или в виде монтажной схемы на макетной плате. Я рекомендую скачать и опробовать это приложение. Иногда желательно протестировать схему отдельно от кода. Например, если мы хотим убедиться, что все наши устройства правильно соединены друг с другом, то можем использовать блок питания макетной платы для питания схемы. Таким образом, если что-то пойдет не так, мы не рискуем
256  Электроника для начинающих повредить наше IoT-устройство. Большинство блоков питания для макетных плат построены на небольшой печатной плате с цилиндрическим разъемом для внешнего адаптера со встроенной вилкой, двумя наборами контактов для подключения к шинам питания на макетной плате и выключателем (очень удобно). Некоторые могут генерировать разное напряжение. На рис. 7.26 показан один из моих любимых блоков питания для макетных плат, доступных на SparkFun (www.sparkfun.com/products/13157). Рис. 7.26  Блок питания для макетной платы (с разрешения sparkfun.com) Если схеме требуется больше места, чем доступно на одном макете, вы можете использовать несколько макетных плат, просто протянув шины питания и продолжив сборку схемы. Чтобы облегчить этот процесс, некоторые макетные платы можно состыковывать с помощью небольших выступов и пазов сбоку. Наконец, большинство макетных плат также имеют клейкую основу, которую можно использовать для установки на основание, на дно внутри корпуса или аналогичного рабочего пространства. Если вы решите использовать клейкую основу, имейте в виду, что потом ее нелегко отклеить – она довольно хорошо держится. Теперь, когда мы знаем больше о том, как работать с макетами, давайте обсудим компоненты, которые наши IoT-решения будут использовать для сбора данных: датчики. Что такое датчики? Датчик – это устройство, измеряющее параметры явлений физического мира. Такими явлениями могут быть сущности, которые вы видите, например свет, дым, водяной пар и т. д. Есть также сущности, которые вы чувствуете, на-
Что такое датчики  257 пример температура, влажность, скорость ветра и т. д. У людей есть чувства, которые действуют как датчики, позволяя нам познавать мир вокруг нас. Однако есть некоторые вещи, которые ваши органы чувств не могут увидеть или почувствовать, например радиацию, радиоволны, напряжение и силу тока. При измерении этих явлений задача датчика – передать результат измерения в той или форме: например, величины напряжения или сразу числа. Существует множество форм датчиков. Обычно это недорогие устройства, предназначенные каждый для своей цели и с ограниченными возможностями обработки. Большинство простых датчиков представляют собой дискретные компоненты; даже те, у которых более сложное устройство, можно рассматривать как отдельные компоненты. Датчики бывают аналоговыми или цифровыми и обычно проектируются для измерения только чего-то одного. Но все большее количество сенсорных модулей предназначено для измерения ряда связанных параметров, таких как Weather Shield от SparkFun Electronics (www.sparkfun.com/products/10586). В следующих разделах рассматривается, как датчики измеряют данные, как хранить эти данные, а также приводятся примеры некоторых распространенных датчиков. Как датчики измеряют Датчики – это электронные устройства, которые генерируют некую электрическую величину на основе уникальных свойств своей химической и механической конструкции. Одно из распространенных заблуждений относительно датчиков заключается в сомнениях, что они не манипулируют параметрами (не изменяют событие или данные), для измерения которых предназначены. На самом деле датчики просто реагируют на некоторую физическую величину и преобразуют ее в пропорциональный электрический сигнал (напряжение, ток, цифровое значение и т. д.)1. Например, датчик влажности измеряет концентрацию воды (влаги) в воздухе. Датчики влажности реагируют на это явление и генерируют напряжение, которое затем может считывать микроконтроллер или подобное устройство. Типичный датчик влажности считывает данные сразу в величинах шкалы влажности, как, например, недорогой датчик влажности и температуры DHT20 (www.adafruit.com/product/5183). На рис. 7.27 показан датчик DHT-20. DHT-20 предназначен для измерения температуры и влажности. Он генерирует цифровой сигнал на выходе (вывод данных). Несмотря на простоту использования, он несколько медленный, и его следует использовать для отслеживания данных с достаточно низкой скоростью (не чаще, чем раз в три или четыре секунды). 1 Что в общем случае неправда – вертушка, измеряющая скорость течения в трубе, вызывает сопротивление потоку; термометрический датчик отводит тепло от измеряемого тела и т. д. Конечно, в каждом отдельном случае влияние датчика стараются свести к минимуму, но эту особенность процесса измерений в общем случае нужно учитывать. – Прим. перев.
258  Электроника для начинающих Рис. 7.27  Датчик влажности DHT-20 (с разрешения adafruit.com) Когда этот датчик генерирует данные, эти данные передаются в виде серии высокого (интерпретируемого как 1) и низкого (интерпретируемого как 0) уровней напряжений, которые микроконтроллер может считывать и использовать для формирования цифрового значения. В данном случае микроконтроллер считывает с датчика значение длиной 40 бит (40 импульсов высокого или низкого уровня, то есть 5 байт) и помещает его в программную переменную. Первые два байта – это значение влажности, вторые два – значение температуры, а пятый байт – значение контрольной суммы, обеспечивающее безошибочное считывание. Вся непростая работа по расшифровке данных выполняется за вас специальной библиотекой, предназначенной для DHT-20 и аналогичных датчиков с таким же форматом данных. В результате DHT-20 выдает цифровое значение. Не все датчики делают это; некоторые вместо этого генерируют только значение напряжения на выходе. Это так называемые аналоговые датчики1. Давайте потратим немного времени, чтобы понять различия. Это станет важной информацией при планировании и создании узлов с использованием датчиков. Аналоговые датчики Аналоговые датчики – это устройства, генерирующие диапазон напряжения, обычно от 0 до 5 вольт. Для преобразования напряжения в число необходим аналого-цифровой преобразователь. Но все не так просто (а было ли так когда-нибудь?). Аналоговые датчики часто работают как переменные резис­ торы, и при подключении к контактам GPIO тогда требуется другой резис­ тор для «подтягивания» или «заземления» выхода, чтобы получить именно величину напряжения. 1 Это может быть не только значение напряжения: есть датчики с токовым выходом; есть с выдачей временного интервала или частоты импульсов и т. д. Все они также относятся к аналоговым датчикам, так как зависимость выходной величины от измеряемой представляет собой непрерывное (аналоговое) значение. Далее автор рассматривает только датчики с выдачей величины напряжения. – Прим. перев.
Что такое датчики  259 При этом напряжение, протекающее через резисторы, непрерывно как по времени, так и по амплитуде. Таким образом, даже когда датчик не успел провести измерение, через его выход все равно проходит ток, который может вызвать ложные показания. Наши проекты требуют четкого различия между состояниями OFF (нулевое напряжение) и ON (положительное напряжение). Подтягивающие и понижающие резисторы гарантируют наличие одного из этих двух состояний. В обязанности аналого-цифрового преобразователя входит получение напряжения, считываемого с датчика, и преобразование его в значение, которое можно интерпретировать как данные. При выборке (когда значение считывается с датчика) считанное напряжение должно интерпретироваться как значение в диапазоне, указанном для данного датчика. Помните, что значение, скажем, 2 В от одного аналогового датчика может не означать то же самое, что 2 В от другого аналогового датчика. В таб­ лице данных каждого датчика показано, как интерпретировать эти значения. Как видите, работать с аналоговыми датчиками сложнее, чем с цифровым датчиком DHT-20. Немного попрактиковавшись, вы обнаружите, что большинство аналоговых датчиков несложно использовать, если вы поймете, как подключить их к микроконтроллеру и как интерпретировать их напряжение по шкале, в которой датчик откалиброван для работы. Цифровые датчики Цифровые датчики, такие как DHT-20, предназначены для создания строки битов с использованием последовательной передачи (по одному биту за раз). Однако некоторые цифровые датчики передают данные параллельной передачей (один или несколько байтов1 за раз). Как описано ранее, биты представлены в виде уровней напряжения, где высокий уровень (скажем, 5 В), или ON, равен 1, а низкий уровень (0 В), или OFF, равен 0. Эти последовательности значений ON и OFF называются дискретными значениями, потому что датчик выдает только то или другое – он либо ON, либо OFF. Цифровые датчики могут измерять чаще, чем аналоговые, поскольку они генерируют данные быстрее и для считывания значений не требуется никаких дополнительных схем (таких как аналого-цифровые преобразователи и логика или программное обеспечение для преобразования значений в нужном диапазоне)2. Таким образом, цифровые датчики, как правило, более 1 2 Это зависит от ширины параллельного буфера. 8-битный буфер может передавать по 1 байту за раз, 16-битный буфер может передавать по 2 байта за раз и т. д. – Прим. авт. Надо учитывать, что чудес не бывает – упомянутые преобразователи и логика просто включены внутрь цифрового датчика. Поэтому никаких особенных преимуществ цифровые датчики не имеют, кроме, разумеется, удобства подключения. Следует помнить то, что часто упускают из виду авторы-программисты: индивидуальной калибровки требуют как одни, так и другие, за исключением очень дорогих образцовых средств. Заводская калибровка, зафиксированная в библиотеках для цифровых датчиков, как правило, не дает удовлетворительных результатов, и безого­ворочно верить ей не следует. – Прим. перев.
260  Электроника для начинающих точны и надежны, чем аналоговые. Но точность цифрового датчика прямо пропорциональна количеству битов, которые он использует для выборки данных1. Наиболее распространенной разновидностью цифрового датчика является кнопка или переключатель. Что, кнопка – это датчик? Да, конечно. Рассмотрим на минутку датчик, установленный на окно в домашней системе безопасности. Это простой выключатель, который замыкается, когда окно закрыто, и размыкается, когда окно открывается. Измерение показывает положительное напряжение (с помощью подтягивающего резистора), когда окно открыто и переключатель разомкнут, но когда окно закрывается, то выключатель замыкается на общий провод (измерение показывает 0 В). Это самый простой из датчиков ON–OFF. Большинство цифровых датчиков представляют собой небольшие схемы из нескольких компонентов, предназначенные для генерации цифровых данных. В отличие от аналоговых датчиков, считывать их данные легко, поскольку значения можно использовать напрямую, без преобразования. Некоторые могут предположить, что это сложнее, чем использовать аналоговые датчики, но это зависит от вашей точки зрения. Энтузиаст электроники сочтет работу с аналоговыми датчиками более простой, тогда как программист сочтет, что цифровые датчики проще в использовании. Теперь давайте посмотрим на некоторые доступные датчики и типы параметров, которые они измеряют. Примеры датчиков IoT-устройство, которое что-то наблюдает, может использовать как минимум один датчик и средство для чтения и интерпретации данных. Возможно, вы думаете о всевозможных полезных вещах, которые можно измерить у себя дома или в офисе либо даже во дворе или в окрестностях. Вероятно, вы захотите измерить изменения температуры в вашем новом солярии и определить, когда почтальон бросил проспект в ваш почтовый ящик, или, может быть, ведете журнал того, сколько раз ваша собака пользуется своей собачьей дверью. Я надеюсь, что теперь вы понимаете, что это лишь верхушка айсберга, когда дело доходит до представления всего того, что можно измерить. Какие типы датчиков доступны? В следующих разделах описаны некоторые из наиболее популярных датчиков и то, что они измеряют. Я также 1 Конечно, автор имеет в виду не точность (accuracy), а разрешающую способность (resolution) – количество знаков в значении выходной величины. Вы запросто можете вывести значение температуры с датчика DNT-20 с тысячными долями (например, 15,125 градуса), но такое разрешение показаний будет заведомой бессмыслицей, так как только специальными физическими методами можно измерить температуру точнее, чем до сотых долей градуса, а обычные бытовые датчики имеют точность плюс-минус 1–2 целых градуса. – Прим. перев.
Примеры датчиков  261 даю несколько советов о том, как вы можете использовать датчик в проекте интернета вещей. Это всего лишь выборка из растущего набора доступных датчиков. Просмотр каталогов онлайн-поставщиков электроники, таких как Mouser Electronics (www.mouser.com), SparkFun Electronics (www.sparkfun. com) и Adafruit Industries (www.adafruit.com), позволит обнаружить еще множество примеров. Я прилагаю еще изображения примеров некоторых типов популярных датчиков. Акселерометры Эти датчики измеряют вращение или перемещение датчика (того, к чему он прикреплен). Акселерометры предназначены для определения движения (скорости, наклона, вибрации и т. д.) по нескольким осям. Некоторые из них имеют гироскопические функции. Большинство из них представляют собой цифровые датчики. Wii Nunchuck (или WiiChuck)1 содержит сложный акселерометр для отслеживания движений. Ага, теперь вы знаете секрет этих забавных штуковин, которые идут в комплекте с вашей консолью! Возможно, вы захотите добавить акселерометры, если ваш IoT-проект включает в себя что-то в движении и наблюдение за этим движением дает полезную информацию. Аудиодатчики Наверное, это очевидно, но для обнаружения звука используются микрофоны. Большинство из них являются аналоговыми устройствами, но некоторые из лучших датчиков безопасности и наблюдения имеют цифровые варианты для более высокого сжатия передаваемых данных. Проекты интернета вещей, такие как домашняя безопасность, наблюдение за детьми или связанные со здоровьем слуховых органов, могут использовать аудиодатчики. Считыватели штрих-кодов Эти датчики предназначены для считывания штрих-кодов. Чаще всего устройства считывания штрих-кода генерируют цифровые данные, представляющие собой числовой (или символьный) эквивалент штрих-кода. Такие датчики используются в системах отслеживания складских запасов оборудования на заводе или во время транспортировки. Их разнообразие велико, и многие из них имеют экономичную цену, что позволяет вам включать их в свои соб1 Wii Nunchuck – контроллер с дистанционным управлением для игровой консоли Nintendo Wii U. – Прим. перев.
262  Электроника для начинающих ственные проекты. Если ваш проект интернета вещей требует сбора данных с объекта, вы можете рассмотреть возможность использования штрих-кодов. Например, если вы хотите определять, когда абоненты парковки входят или выходят с необслуживаемой парковки, вы можете разместить у ворот подобное устройство, которое считывает штрих-коды, которые вы разрабатываете и распространяете среди подписчиков вашего сервиса. Когда автомобиль подъезжает к воротам, считыватель штрих-кода может прочитать штрих-код, сравнить с зарегистрированными образцами и поднять ворота. Если вы когда-либо жили в большом городе, работали в контролируемом офисном комплексе или учились в университете, возможно, вы сталкивались с подобными решениями для парковки. Биометрические датчики Устройство, считывающее отпечатки пальцев или рисунок радужной оболочки глаз, содержит специальный датчик, предназначенный для распознавания узоров. Учитывая уникальность, присущую таким узорам, как отпечатки пальцев и ладоней, они являются отличными компонентами для системы безопасного доступа. Большинство биометрических датчиков создают блок цифровых данных, который представляет отпечаток пальца или ладони, переведенный в цифру. Проекты интернета вещей, требующие высокого уровня безопасности, могут включать биометрический датчик, который поможет идентифицировать пользователя системы. Емкостные датчики Датчики пульса, представляющие собой особое применение емкостных датчиков, предназначены для измерения частоты пульса и обычно используют кончик пальца в качестве места измерения. Специальные устройства, известные как пульсоксиметры (некоторые медицинские работники называют их «пульс-окс»), измеряют частоту пульса с помощью емкостного датчика и определяют содержание кислорода в крови с помощью датчика освещенности. Если у вас есть современные электронные устройства, вы, возможно, сталкивались с сенсорными кнопками, которые используют специальные емкостные датчики для обнаружения прикосновения и давления. Если вашему проекту необходимо измерять какие-либо движения или реагировать на прикосновения, емкостные датчики могут помочь обеспечить футуристический бесконтактный интерфейс. Сенсорная панель последнего MacBook Pro – пример такого решения. На рис. 7.28 показаны два примера сенсорных модулей, которые можно купить в Adafruit: сенсорная кнопка без фиксации (www.adafruit.com/product/1374) и сенсорный переключатель (www.adafruit.com/product/1375).
Примеры датчиков  263 Рис. 7.28  Платы модулей с сенсорными емкостными датчиками (с разрешения adafruit.com) Датчики монет (монетоприемники) Это один из самых необычных типов датчиков. Эти устройства (www.sparkfun. com/products/11719) похожи на отделения для монет в обычном торговом автомате. Как и их коммерческий аналог, их можно откалибровать, чтобы определять, когда вставляется монета определенного размера. Хотя эти датчики монет не так сложны, как коммерческие устройства, которые могут отличать поддельные монеты от настоящих, их можно использовать, чтобы добавить новое качество вашим проектам. Отличным и практичным проектом для родителей могла бы стать Wi-Fi-станция с монетоприемником, где детям придется покупать собственное время в интернете. Это не только убережет их от слишком частого использования интернета, но и поможет научить их планировать свои карманные расходы. Это должно удержать детей проводить в интернете слишком много времени! Датчики тока Они предназначены для измерения силы тока (и иногда напряжения). Некоторые из них предназначены для контроля изменений, тогда как другие измеряют ток в нагрузке. Проекты, содержащие компоненты, нуждающиеся в контроле за потреблением электроэнергии, потребуют датчика тока. Это могут быть любые проекты, так как вы можете использовать такие датчики для мониторинга поведения существующих решений, не вмешиваясь в них. Например, если вы хотите адаптировать датчики для наблюдения за производственным оборудованием, то можете добавить к различным компо-
264  Электроника для начинающих нентам датчики, контролирующие ток. То есть вы можете записывать, когда напряжение подается на двигатели, исполнительные механизмы или даже сигнальные лампы, чтобы определить, когда (или насколько часто) активируются устройства. Однако как любитель вы, скорее всего, можете быть заинтересованы в создании собственного мультиметра или аналогичного инструмента. Датчики изгиба/силы (тензорезистивные) Тензорезистивные датчики измеряют изгибы куска материала и могут показать величину изгибающей силы, в том числе воздействие давления на датчик. Датчики изгиба могут быть полезны для измерения эффектов кручения или для измерения движений пальцев (например, в перчатках Nintendo Power Glove). Выходной сигнал тензорезистивного датчика увеличивается, когда датчик сгибается. Например, если вы хотите создать IoT-решение для рыбалки, то можете использовать такой датчик на своей удочке, чтобы в режиме реального времени получать сигналы, когда рыба клюет на приманку. Датчики газа Существует великое множество типов газовых датчиков. Некоторые измеряют содержание потенциально вредных газов, таких как сжиженный газ и метан, также есть для других газов, таких как водород, кислород и т. д. Иногда датчики газа объединяют с датчиками освещенности для обнаружения дыма или загрязняющих веществ в воздухе. В следующий раз, когда вы услышите этот раздражающий звуковой сигнал, предупреждающий о низком заряде батареи, из вашего детектора дыма1, подумайте о том, что содержит это устройство. Ведь это же целый сенсорный узел! Если вашему IoT-проекту необходимо обнаруживать любой газ в воздухе, особенно если он предполагает реакцию на определенные газы или их уровни, вам необходимо будет использовать соответствующие датчики газа. Датчики света Датчики, измеряющие интенсивность освещенности, представляют собой специальные типы резисторов: светозависимые резисторы (LDR), иногда 1 Я придерживаюсь теории «чем больше, тем лучше», и у нас дома много детекторов, и это здорово! Но когда батарейки разряжаются, я никогда не могу сказать, какой именно детектор подает звуковой сигнал! Это становится безумно неприятно, особенно когда они подают звуковой сигнал всего один или два раза, а затем замолкают. К счастью, я заменил питание почти во всех из них на новейшие варианты батарей со сроком службы десять лет. – Прим. авт.
Примеры датчиков  265 называемые фоторезисторами или фотоэлементами 1. Таким образом, они являются аналоговыми по своей природе. Если у вас есть ноутбук Mac, скорее всего, вы видели фотодатчик в действии, когда клавиатура с подсветкой включается при слабом освещении. Специальные разновидности датчиков освещенности могут обнаруживать другие области светового спектра, например инфракрасные (как в ИК-пультах от телевизоров). Например, если вы хотите, чтобы ваш проект автоматически регулировал яркость дисплея, датчик освещенности – это тот компонент, который вам нужен. На следующих рисунках показаны два примера фотодатчиков. На рис. 7.29 показан типичный мини-фоторезистор (www.sparkfun.com/products/9088). Рис. 7.29  Мини-фоторезистор (с разрешения сайта sparkfun.com) На рис. 7.30 показана плата модуля, объединяющая датчики приближения, освещенности и RGB (цвета) на одной небольшой плате (www.adafruit.com/ product/3595). 1 Многие датчики освещенности, особенно предназначенные для работы в режиме ON–OFF (включения–выключения при заданном уровне света), выполнены на основе фотодиодов, фототранзисторов или даже фототиристоров – они гораздо чувствительнее фоторезисторов, единственным достоинством которых является линейная зависимость сопротивления от освещенности в широком диапазоне изменения интенсивности света, составляющем несколько порядков. Фоторезистор показан на рис. 7.29; датчики в составе APDS-9960 (рис. 7.30) основаны на других разновидностях. В свою очередь, фотоэлементом называют специальный электровакуумный прибор, в котором ток увеличивается при росте освещенности за счет внешнего фотоэффекта. Прямого отношения к современным полупроводниковым фотодатчикам, основанным на внутреннем фотоэффекте, классический фотоэлемент не имеет. Подробности см. https://ru.wikipedia.org/wiki/Фотоэффект. – Прим. перев.
266  Электроника для начинающих Рис. 7.30  Модуль датчиков приближения, освещенности и RGB (с разрешения adafruit.com) Датчики расхода жидкости Эти датчики напоминают вентили и устанавливаются в водопроводных системах. Они измеряют проходящий расход потока жидкости. Простейшие датчики расхода используют вращающееся колесико с лопастями и магнит для срабатывания датчиков Холла (на выходе имеем последовательность включений/выключений, частота которых соответствует количеству прошедшей воды). Если ваш проект включает в себя любую форму текущей жидкости, например наполнение садового пруда или ирригационную систему, знание потока воды может быть полезно для управления или наблюдения. Датчики уровня жидкости Для измерения относительного уровня воды можно использовать специальное резистивное твердотельное устройство. Датчик показывает низкое сопротивление, когда уровень воды высокий, и более высокое сопротивление, когда уровень низкий. Датчики уровня жидкости обычно используются в одном и том же решении с датчиками расхода жидкости. На рис. 7.31 показан типичный релейный датчик уровня жидкости, который работает как переключатель: поплавок замыкает переключатель при повышении уровня воды.
Примеры датчиков  267 Рис. 7.31  Датчик уровня воды Датчики местоположения Современные смартфоны оснащены датчиками GPS для определения местоположения и, конечно же, используют технологию GPS, чтобы помочь вам ориентироваться. К счастью, датчики GPS доступны отдельно в недорогих вариантах, что позволяет добавить определение местоположения в ваш проект. Датчики GPS генерируют цифровые данные в виде долготы и широты, и большинство из них также могут определять высоту над уровнем моря. Если вашему проекту необходимо сообщать о своем местоположении, датчик GPS может дать очень точные показания. Однако, как и большинство датчиков, датчики GPS могут иметь некоторую неточность. В зависимости от того, насколько точно вам нужно что-то определять, возможно, придется потратить немного больше денег на более точный GPS-датчик. Считыватели магнитных полос Эти датчики считывают данные с магнитных полос (например, на старой кредитной карте) и возвращают буквенно-цифровые данные. Проекты, включающие элементы безопасности, могут использовать считыватель магнитных полос, чтобы идентифицировать пользователя. В сочетании с паролем и биометрическим датчиком безопасность доступа может быть значительно повышена. То есть кто-то должен что-то знать (пароль или PIN-код), чем-то обладать (картой безопасности с магнитной полосой, закодированной клю-
268  Электроника для начинающих чевой фразой, номером, идентификатором пользователя и т. д.)1 и пройти проверку личности (отпечаток пальца), прежде чем получить доступ. Магнитометры Магнитометрические датчики измеряют ориентацию с помощью магнитных полей. Компас – это датчик для определения магнитного севера. Некоторые магнитометры имеют несколько осей, что позволяет еще точнее определять ориентацию в пространстве. Это еще один датчик, с которым вы, возможно, не часто сталкиваетесь, но если вашему проекту необходимо получать географическое направление, возможно, вам стоит обратить внимание на магнитометры. Датчики влагосодержания Датчики влагосодержания измеряют количество влаги в веществе (например, почве). Обычно они отправляют данные в виде показаний напряжения, где низкие значения указывают на меньшую влажность. Датчики влагосодержания часто встречаются в атмосферных проектах или даже в решениях для мониторинга предприятий. На рис. 7.32 показан типичный датчик влагосодержания почвы (www.sparkfun.com/products/17731). Обратите внимание, что зубцы – это часть датчика, вставляемая в почву. Рис. 7.32  Датчик влагосодержания почвы (с разрешения sparkfun.com) 1 В настоящее время считыватели магнитных полос успешно заменяются радиочастотными (RFID) метками (карточками, см. «RFID-датчик» далее), а также всем знакомыми по домофонам электронными контактными ключами (с интерфейсом 1-Wire). И то, и другое решение – гораздо более долговечное и устойчивое к вандализму и воздействию окружающей среды, чем магнитная полоса. – Прим. перев.
Примеры датчиков  269 Датчики приближения Датчики приближения, в том числе датчики расстояния, используют инфракрасные или звуковые волны для определения движения объекта или расстояния до него. Ультразвуковой датчик Parallax Ultrasonic Sensor, ставший популярным благодаря недорогим комплектам робототехники, использует звуковые волны для измерения расстояния, определяя промежуток времени между отправленным и полученным импульсами (эхо). Для приблизительного измерения расстояния с помощью этого датчика1 необходимо решить простую математическую задачу: преобразовать время в расстояние. Если вы создаете проект, который обнаруживает движение или близость объекта, вы можете использовать датчики приближения. На следующих рисунках показаны два типа датчиков приближения. На рис. 7.33 представлен популярный датчик движения с пассивным инфракрасным датчиком (PIR-датчиком2) (www.sparkfun.com/products/13285). Рис. 7.33  PIR-датчик движения (с разрешения sparkfun.com) На рис. 7.34 показан ультразвуковой датчик (www.sparkfun.com/products/ 15569), используемый во многих проектах, от роботов до дронов. 1 2 Точность может зависеть от параметров окружающей среды, таких как высота над уровнем моря, давление, температура и т. д. – Прим. авт. PIR-датчик (действие которого всем знакомо по автоматическим дверям в супермаркетах и торговых центрах) использует специальный пироэлектрический эффект, который гораздо чувствительнее обычного фотоэффекта в ИК-области. PIR-датчики реагируют не на абсолютную интенсивность ИК-излучения, а только на изменения температурного поля объекта, и могут фиксировать перепад в миллионную долю градуса. Немаловажное их достоинство – независимость функцио­ нирования от температуры окружающей среды, они одинаково эффективны на морозе минус 40 градусов и на жаре плюс 40. – Прим. перев.
270  Электроника для начинающих Рис. 7.34  Ультразвуковой датчик расстояния (с разрешения sparkfun.com) Датчики радиации Среди более серьезных датчиков есть те, которые обнаруживают проникающую радиацию. Это также может быть электромагнитное излучение (для этого тоже есть датчики), но счетчик Гейгера используется для обнаружения вредной ионизации за счет радиации. Фактически можно построить свой собственный счетчик Гейгера, используя датчик, Arduino и несколько элект­ ронных компонентов. Это один из датчиков, с которым вы как любитель можете не столкнуться. Существует несколько наборов для сборки собственного счетчика Гейгера, например от Adafruit (www.adafruit.com/product/483). На рис. 7.35 показан комплект в сборе. Рис. 7.35  Комплект счетчика Гейгера – датчик радиации (с разрешения adafruit.com)
Примеры датчиков  271 RFID-датчики Радиочастотная идентификация использует пассивное энергонезависимое устройство (иногда называемое RFID-меткой) для передачи данных с использованием радиочастот посредством электромагнитной индукции. Например, RFID-метка может представлять собой пластиковую карту размером с кредитную карту, этикетку или что-то подобное, содержащее специальную антенну, обычно в виде катушки, тонкой проволоки или слоя фольги, настроенную на определенную частоту. Когда метка находится рядом со считывателем, считыватель излучает радиосигнал; метка использует энергию электромагнитного поля для передачи сообщения через встроенную антенну, в виде радиосигналов, которые затем преобразуются в буквенно-цифровые символы1. RFID-датчики – еще один отличный выбор для систем безопасности. Если у вас есть домашние животные, вы можете посетить своего ветеринара, чтобы узнать о RFID-датчиках, которые действуют как скрытые метки идентификации владельца. Если вы знаете код, вы даже можете использовать такую метку, чтобы определить, когда ваш питомец проходит через дверь для домашних животных. На рис. 7.36 показан считыватель RFID (датчик), который можно использовать для считывания RFID-меток через USB (www.sparkfun.com/products/9963). Рис. 7.36  USB-считыватель RFID Датчики скорости Как и датчики расхода, простые датчики скорости, подобные тем, что установлены на многих велосипедах, используют магнит и датчик Холла. Частоту в сочетании с длиной окружности колеса можно использовать для расчета 1 http://en.wikipedia.org/wiki/Radio-frequency_identification. – Прим. авт. То же на русском https://ru.wikipedia.org/wiki/RFID. – Прим. перев.
272  Электроника для начинающих скорости и, с течением времени, пройденного расстояния. Если вашему IoTрешению необходимо считывать движение, вы можете использовать подобный принцип для обнаружения вращения. Например, в велосипедных спидометрах часто используются магнит и датчик Холла для определения частоты оборотов, исходя из окружности колеса, производится расчет скорости. Переключатели и кнопки Это самые простые датчики, используемые в цифровой технике для определения того, установлен уровень напряжения (ON) или сброшен (OFF). Вы можете использовать переключатели и кнопки для создания пользовательского интерфейса, для управления другими устройствами или даже для включения устройства! Датчики наклона Эти датчики могут определять, когда устройство наклонено в ту или иную сторону. Хотя они очень просты, они могут быть полезны для недорогих датчиков обнаружения движения. По сути, они представляют собой переключатели. Если вашему IoT-решению необходимо определять наклон устройства, вы можете использовать датчики наклона для срабатывания при определенном угле наклона. Например, на некоторых современных мотоциклах для включения сигналов поворота используются датчики наклона, при этом фары расположены под углом, чтобы улучшить обзорность поворота в ночное время. Сенсорные датчики Сенсорные мембраны, образующие клавиатуры, указывающие устройства и т. п., представляют собой интересную форму датчиков. Подобные сенсорные устройства можно использовать в человеко-машинном интерфейсе. Сенсорные датчики могут помочь вам создать пользовательский интерфейс для вашего IoT-проекта, который занимает минимальное место по высоте и экономит габариты консоли или корпуса и т. д. Видеодатчики Как упоминалось ранее, можно получить очень маленькие видеодатчики, которые используют камеры и схемы для захвата изображений для передачи их в виде цифровых данных. Если вы хотите включить видеоэлемент в свой проект, например в решение по безопасности, то можете добавить камеру
Примеры датчиков  273 или видеодатчик для захвата визуального компонента, который может помочь предоставить информацию, выходящую за рамки измерений обычных датчиков. То есть вы можете просмотреть фотографию и узнать больше, чем просто что-то переместилось или приблизилось к устройству. Например, вы можете создать проект, который обнаруживает движение и фотографирует, если что-то приближается достаточно близко или, возможно, движется быст­ рее определенного порога1. Датчики погоды (метеодатчики) Датчики температуры, барометрического давления, осадков, влажности, скорости ветра и т. д. классифицируются как датчики погоды. Большинство из них генерируют цифровые данные и могут быть объединены для создания комплексных экологических решений. На рис. 7.37 показан обычный выносной модуль датчика давления и температуры BMP280 (www.adafruit.com/ product/2651). Рис. 7.37  Датчик давления и температуры BMP280 (с разрешения adafruit.com) С помощью этого и других простых в использовании датчиков можно построить собственную метеостанцию, используя примерно дюжину недорогих датчиков, плату MicroPython и немного программирования для интерпретации и объединения данных. Фактически мы займемся этим в главе 10! 1 Вы когда-нибудь думали, что было бы здорово, если бы смогли сфотографировать животное, пожирающее ваш урожай в саду? Создайте свою специальную камеру для животных с датчиком приближения и инфракрасной камерой для ночной съемки! – Прим. авт.
274  Электроника для начинающих СОВЕТ Если вы хотите увидеть больше датчиков, то можете сделать это на сайтах Adafruit (www.adafruit.com/category/35) или SparkFun (www.sparkfun.com/categories/23)1. Я хочу узнать больше! Если вы обнаружите, что вам нужно или вы хотите узнать больше об электронике, чем то, что я представил в этой главе, или вы хотите узнать больше об электронике, которая вам понадобится для более продвинутого проекта интернета вещей, вы можете рассмотреть возможность прохождения курса в общественном колледже или попробовать найти курс по электронике для самостоятельного изучения. Один из лучших курсов для самостоятельного обучения, который я нашел, включает набор книг по электронике Чарльза Платта. Я обнаружил, что эти книги очень хорошо написаны и открывают многим возможность изучать электронику без необходимости тратить годы на изучение утомительной (хотя и важной) теории и математики электроники. И самое приятное то, что они не написаны в унылом хрестоматийном темпе «факт–факт–факт–вопрос». Они написаны экспертом с мировым именем, обладающим даром изложения материала в доступной для чтения и понимания форме. Я рекомендую следующие книги всем, кто хочет узнать больше об электронике: • Make: Electronics, Third Edition (O’Reilly, 2021), Charles Platt (русский перевод: Платт Чарльз. Электроника для начинающих. 3-е изд. СПб.: BHV, 2024); • Make: More Electronics (O’Reilly, 2014), Charles Platt (русский перевод: Платт Чарльз. Электроника. Логические микросхемы, усилители и датчики. СПб.: BHV, 2020); • Encyclopedia of Electronic Components Volume 1 (O’Reilly, 2012), Charles Platt (русский перевод: Платт Чарльз. Энциклопедия электронных компонентов. Т. 1. СПб.: BHV, 2017); • Encyclopedia of Electronic Components Volume 2 (O’Reilly, 2014), Charles Platt (русский перевод: Платт Чарльз, Янссон Фредрик. Энциклопедия электронных компонентов. Т. 2. СПб.: BHV, 2016); • Encyclopedia of Electronic Components Volume 3 (O’Reilly, 2016), Charles Platt (русский перевод: Платт Чарльз, Янссон Фредрик. Энциклопедия электронных компонентов. Т. 3. СПб.: BHV, 2017). Третий том его серии энциклопедий включает углубленное исследование датчиков, необходимых для передовых проектов интернета вещей. Итоги Обучение тому, как работать с электроникой в качестве хобби или создавать решения для интернета вещей, не требует целой жизни или смены профес1 Практически все позиции, представленные в каталогах названных организаций, ориентированных на американский рынок, либо сами по себе представлены в оте­ чественных интернет-магазинах, либо имеют более дешевые аналоги. Следует только напомнить, что обращаться к AliExpress, где все то же самое может оказаться совсем дешево, нужно с осторожностью. – Прим. перев.
Итоги  275 сии. Действительно, научиться работать с электроникой – это часть удовольствия от экспериментов с интернетом вещей! Я встречал много людей, которые изучали электронику самостоятельно, и хотя большинство признает, что формальное обучение необходимо для полного освоения этой темы, вы можете многому научиться самостоятельно – достаточно, чтобы разобраться в работе с основными электронными компонентами, которые обычно встречаются в наших проектах. В этой главе были представлены основы электронных компонентов, включая использование макетных плат, общих компонентов и примеры схем. Эти сведения и некоторые знания о том, как пользоваться мультиметром, помогут вам освоить электронику. Мы также узнали об одном из ключевых компонентов IoT-решений – датчиках. Мы обнаружили два способа их подключения (цифровой и аналоговый) и немного о том, какие типы датчиков доступны. В следующей главе мы углубимся в наш первый проект в области электроники – эквивалент традиционному программистскому «Привет, мир!» для проектов с использованием оборудования. Мы увидим, как подключить нашу плату MicroPython к нескольким компонентам и написать программу для управления ими. Отлично!
Глава 8 Проект «Hello, World!» в стиле MicroPython Здесь мы подошли к самой интересной части этой книги – работе над проектами MicroPython. На этом этапе мы уже научились писать на MicroPython и теперь знаем гораздо больше об аппаратном обеспечении и даже о том, как использовать электронные компоненты и внешние модули. Эта глава представляет собой введение в создание проектов MicroPython. Таким образом, нам нужно изучить еще несколько вещей, включая методы и процедуры установки и запуска наших проектов на платах MicroPython. В этой главе мы познакомим вас с тем, что вам нужно для успеха ваших проектов. Таким образом, глава немного длиннее необходимого и ее следует считать обязательной к прочтению, даже если вы не планируете реализовывать изложенный здесь проект. Как вы увидите, формат всех глав с проектами в целом одинаков. Представлен обзор проекта, за которым следует список необходимых компонентов и способы сборки оборудования. Как только поймем, как подключается оборудование, мы начнем писать код. Каждая глава завершается описанием того, как реализовать проект, а также примером его выполнения и предложениями по его усовершенствованию. Прежде чем мы приступим к нашему первому проекту электроники для Pico, давайте обсудим несколько практических советов по разработке проектов. Они применимы ко всем проектам в этой книге и, вероятно, к любому будущему проекту, который вы можете захотеть реализовать. Начало работы с проектами MicroPython Если вы никогда раньше не работали с микроконтроллерами, у вас нет знаний о создании проектов в области электроники или вы незнакомы с аппа-
Начало работы с проектами MicroPython  277 ратным обеспечением Pico, вам может быть интересно, как начать создавать такие проекты. В этом разделе мы увидим несколько полезных советов и рекомендаций о том, как начать работу с оборудованием Pico. Большая часть этих советов применима к любому проекту в области электроники или микроконтроллера. Они включены сюда для новичков и тех, кому необходимо освежить знания. Один шаг за раз! Мы уже обсуждали (см. главу 3) распространенную ошибку, которую допус­ кают новички, – это сесть и соединить всю свою электронику вместе, а затем написать весь свой код за один проход, ничего не тестируя заранее. Это создает ситуацию, когда, если что-то не работает, оно может маскировать множество разнообразных ошибок. Потому повторим, что проект необходимо создавать поэтапно. То есть создавайте свой проект по одному шагу за раз. Например, если вы работае­ те со светодиодами, чтобы что-то сигнализировать, сначала заставьте их работать. Точно так же, если вы считываете данные с датчика, убедитесь, что вы можете сделать это правильно в отдельности, прежде чем соединять все вместе и надеяться, что все работает. Мы будем строить примеры в этой книге шаг за шагом. Требуется доработка Как мы говорили (см. главу 3), некоторые поставщики предлагают платы Pico и платы внешних модулей с припаянными разъемами или без них. Отсутствие пайки разъемов позволяет сэкономить на производстве, отчего продукт оказывается дешевле. А в некоторых случаях отсутствие разъемов необходимо: если вы хотите установить модуль на кросс-плату или использовать в какой-либо другой форме встроенной установки. В этом случае пайка разъемов может занять больше места, чем у вас есть, или сделать завершенный проект слишком громоздким1. Напомним, что большинство внешних модулей и датчиков от Adafruit (adafruit.com) и SparkFun (sparkfun.com) поставляются с неприпаянными разъемами. 1 Как уже отмечалось в главе 3, есть еще одна важная причина для самостоятельной установки разъемов. Она заключается в том, что игольчатые разъемы бывают различной конфигурации (гнезда или штыри, развернутые под углом 90 градусов, разъемы с удлиненными контактами и т. д.), и ее выбор определяется компоновкой окончательного изделия. В случае установки производителем неподходящего типа разъем придется менять, а замена многоконтактного разъема намного сложнее чис­той установки и у неопытного паяльщика не всегда заканчивается благополучно для платы, над которой производится такое издевательство. – Прим. перев.
278  Проект «Hello, World!» в стиле MicroPython Обращаться осторожно! В главе 3 мы также уже говорили, что электронные компоненты (в том числе платы контроллеров) следует рассматривать как чувствительное устройство, подверженное электростатическому разряду (ESD). Многие платы и компоненты, которые вы покупаете, поставляются в специальной антистатической упаковке. Не выбрасывайте ее – храните купленные компоненты в такой упаковке! Вам следует позаботиться о том, чтобы ваше тело, ваше рабочее место и сам проект были заземлены во избежание электростатического разряда (ESD). В главе 3 упоминался заземляющий браслет на запястье, который крепится к антистатическому коврику. Никогда не следует перемещать плату, если схема включена. Почему? Любые платы имеют припаянные компоненты со множеством контактов, открытых с обеих сторон. Если любые два или более из этих контактов соприкоснутся с чем-то, проводящим электричество, вы рискуете повредить плату. Загляните в главу 3, раздел «Начало работы с платами MicroPython», чтобы освежить подробности об этих и других рекомендациях по обращению с электроникой. Теперь давайте приступим к нашему самому первому проекту MicroPython! Обзор В этой главе мы спроектируем и создадим часы MicroPython. Мы будем использовать модули с интерфейсами SPI и I2C: небольшой дисплей на органических светодиодах (OLED), использующий интерфейс SPI, и аппаратные часы реального времени (RTC), использующие чип DS1307 и резервную батарейку для хранения времени, пока проект выключен. Такие часы не требуют подключения к серверу протокола сетевого времени (NTP) в интернете. Текущая дата и время извлекаются из аппаратных часов реального времени и отображаются на OLED-дисплее, что не только позволяет уменьшить размер проекта, но и демонстрирует, как использовать RTC для проектов, которые не подключены к интернету. Хотя Pico имеет аппаратный RTC, его необходимо инициализировать каждый раз, когда вы подключаете плату к компьютеру (через Thonny или rshell), что делает его далеко не идеальным для проекта устройства, которое вы включаете лишь периодически. Чтобы Pico мог узнать, который час, после выключения и повторного включения без подключения к ПК, мы будем использовать внешний RTC. Как вы увидите, требуется изрядное количество проводов, а для написания кода необходимо понимание аппаратных возможностей, поэтому в предыдущих главах мы уделили время разговору о встроенном программном обес­
Обзор  279 печении и различных низкоуровневых аппаратных средствах управления. Вам понадобятся эти навыки и знания для завершения данного проекта. Хотя часы могут показаться довольно простыми, этот проект проведет вас через все шаги, необходимые для сборки оборудования и написания кода. Кроме того, проект небольшой и упрощенный, поэтому мы можем сосредоточиться на процессе, а затем применить его к более сложным проектам. Фактически мы увидим, что даже относительно простое устройство может иметь неожиданно высокий уровень сложности. Но не волнуйтесь: в этой главе описано все, что нужно сделать для завершения проекта. Источников для этого проекта много. Следующие ссылки включают исходные данные, использованные в проекте, в том числе документацию и ссылки на библиотеку MicroPython (также называемую драйвером), которую нам нужно будет загрузить для этого проекта:   информация об OLED-дисплее: https://learn.adafruit.com/monochromeoled-breakouts/wiring-128x32-spi-oled-display;   библиотека OLED-дисплея: https://github.com/adafruit/micropythonadafruit-ssd1306;   документация по плате модуля RTC: https://learn.adafruit.com/ds1307real-time-clock-breakout-board-kit;   библиотека RTC: https://github.com/adafruit/Adafruit-uRTC. ПРИМЕЧАНИЕ Библиотеки Adafruit MicroPython помечены как устаревшие, что означает лишь то, что никто их активно не поддерживает; однако они будут работать с вашей платой Pico без ограничений. Обратите внимание на используемые сайты. Хорошей практикой будет начать с изучения сайтов Adafruit и MicroPython, блогов и форумов. Если вы найдете хорошие описания по теме, возможно, вы захотите загрузить их на свой компьютер или планшет либо распечатать для дальнейшего чтения. Что еще более важно, найдите время, чтобы прочитать приведенные ссылки, чтобы понять как можно больше, прежде чем начать работать с оборудованием или писать свой код. Вы можете сэкономить много времени, зная простые вещи заранее, например как подключить плату к устройству и как предполагается использовать библиотеку. Какой библиотекой я пользуюсь? Вы можете столкнуться с ситуацией, когда найдете более одной библиотеки для оборудования, которое хотите использовать. На самом деле я нашел несколько библиотек для OLED-дисплея. Различия между ними незначительны: по крайней мере одна из них не поддерживает текст, другая написана для конкретной платформы, а третья написана на C++ для Pico. Та, которая указана по ссылке выше, является лучшей для использования. Для применения в наших целях потребуются некоторые незначительные изменения. Я покажу вам эти изменения, и, как вы увидите, их не так уж сложно исправить (например, когда MicroPython генерирует исключения, он покажет вам источник проблемы).
280  Проект «Hello, World!» в стиле MicroPython Если вы столкнулись с аналогичной ситуацией – имея на выбор более одной биб­ лиотеки, – вы можете попробовать каждую, пока не найдете ту, которая лучше всего подходит для вашего оборудования и проекта. Иногда какая-либо библиотека может оказаться нежизнеспособной, а в другой могут отсутствовать необходимые функции. Хитрость заключается в том, чтобы найти библиотеку, которая работает лучше всего с наименьшим количеством необходимых исправлений. Теперь посмотрим, какие компоненты нужны для этого проекта и как их все соединить между собой. Необходимые компоненты В табл. 8.1 перечислены компоненты, которые вам понадобятся в дополнение к плате Pico. Вы можете приобрести компоненты отдельно в Adafruit (adafruit.com), SparkFun (sparkfun.com) или в любом магазине электроники, где продаются электронные компоненты. Таблица 8.1. Необходимые компоненты Компонент OLEDдисплей RTC-модуль Макетная плата Перемычки Батарейка типа «монетка» Кол-во Описание 1 Дисплей с интерфейсом SPI на основе ssd1306 1 Модуль RTC с резервной батареей1 1 Макетная плата, полноразмерная2 11 Перемычки «штырь/штырь», 6 или 7 дюймов 1 CR1225, CR1220 (CR2032) Ссылки www.adafruit.com/product/661 www.sparkfun.com/products/12708 www.adafruit.com/product/3296 www.sparkfun.com/products/12615 www.sparkfun.com/products/9838 www.adafruit.com/product/1950 www.sparkfun.com/products/337 www.adafruit.com/product/380 (www.chipdip.ru/product0/900135075) Подключаемая плата (модуль) OLED, используемая в этом проекте, представляет собой небольшой модуль от Adafruit. У него небольшой, но яркий дисплей, который можно установить на макетную плату. Разрешение составляет 128 пикселей в ширину и 32 пикселя в высоту. Модуль OLED поставляет1 2 Вместо указанного автором модуля RTC можно выбрать любой другой любого производителя на основе DS1307. Причем желательно приобретать с резервной батареей типа CR2032, так как она встречается в продаже гораздо чаще, чем более редкие типы CR1225-1220, и обладает большей емкостью, а имеющиеся в продаже модули чаще рассчитаны именно на нее. Вывод внешнего прерывания SQW, имеющийся на указанном автором модуле (см. рис. 8.2 далее), в данном проекте не используется, и его наличие необязательно. – Прим. перев. См. сноску в разделе «Макетная плата и перемычки» главы 2. – Прим. перев.
Необходимые компоненты  281 ся без установленных разъемов, но их легко добавить, если вы умеете паять (возможно, сейчас самое время попрактиковаться) или можете попросить друга помочь вам. На рис. 8.1 показана плата Adafruit OLED SPI. Рис. 8.1  Монохромный графический OLED-дисплей SPI 128×32 (с разрешения adafruit.com) Доступно много разных модулей OLED, и если они имеют интерфейс SPI и используют микросхему контроллера ssd1306 (об этом вам расскажет описание), вы можете использовать другой OLED-дисплей. Причина, по которой нам нужно использовать ее именно с данным типом контроллера, заключается в том, что библиотека написана для этого контроллера. Для других контроллеров потребуется другая библиотека. Плата модуля RTC, используемая в этом проекте, представляет собой плату DS1307 от Adafruit. Плата также поставляется без установленных разъемов (но они входят в комплект поставки) и не поставляется с батареей, поэтому вам необходимо приобрести батарейку типа «монетка». На рис. 8.2 показан модуль RTC. Рис. 8.2  Модуль часов реального времени DS1307 (с разрешения adafruit.com)
282  Проект «Hello, World!» в стиле MicroPython Доступно много разнообразных модулей часов реального времени DS1307. Причем вы можете создать свой собственный! Дополнительные сведения см. во врезке «Создание собственного модуля RTC» (в разделе «А что дальше?» далее). К счастью, библиотека, которую мы будем использовать, поддерживает любые RTC-модули с чипами DS1307, DS3231 или PCF8523. Теперь давайте посмотрим, как объединить компоненты вместе. Настройка оборудования Этот проект имеет много соединений. Для OLED необходимо подключить семь линий, а для RTC – четыре. Чтобы упростить задачу, мы спланируем, как все должно быть соединено. Для установки плат модулей мы будем использовать полноразмерную макетную плату, чтобы упростить соединения. Для выполнения этих соединений мы будем использовать перемычки «штырь/ штырь». Мы узнаем, как подключить Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как вы увидите, соединения похожи тем, что подключаются платы с одинаковыми разъемами и количеством проводов, но места подключения к платам контроллеров различаются. Соединения для Raspberry Pi Pico Но сначала мы определим, какие соединения необходимы для каждого компонента и где их нужно подключить к плате контроллера, записав их в таб­ лицу, чтобы все было понятно. Выполнение этого домашнего задания небольшого объема сэкономит вам время в дальнейшем. Как вы увидите, такое отображение соединений упрощает их проверку. Таб­лица вместе со схемой подключения (монтажной схемы) – это инструмент, который вы увидите в данной книге и других примерах проектов в интернете или в других книгах1. Умение составлять и читать монтажные схемы – это навык, который необходим для успеха проекта. В табл. 8.2 показаны соединения, необходимые для этого проекта. Традиционно мы используем черный цвет для общего провода (отрицательный вывод источника питания) и красный для плюса питания (положительный вывод), но вы можете использовать провода любого цвета по вашему желанию. 1 Строго говоря, достаточно только монтажной схемы подключения (или даже только принципиальной) – при условии что соединяющиеся выводы всех компонентов схемы достаточно подробно и без разночтений обозначены в соответствии с реальными обозначениями на платах модулей или в описаниях компонентов. Можете обратить внимание, что таблицы соединений в примерах далее по сути дублируют информацию, уже имеющуюся на схемах, не внося ничего нового (а если бы не дублировали, это бы приводило только к путанице). – Прим. перев.
Настройка оборудования  283 Мы начнем с физического контакта номер 40 платы Pico и дойдем до самого низкого номера из используемых. Как видите на рисунках разводки выводов (см. рис. 3.3), нумерация убывает по часовой стрелке, начиная справа сверху. Таблица 8.2. Соединения для часов MicroPython (Pico) Физический контакт (Pico) 40 38 38 36 26 25 24 22 21 12 11 Название/номер GPIO (Pico) VBUS GND GND 3V3 GP20 MOSI (GP19) SCK (GP18) GP17 GP16 GP9 GP8 Плата модуля RTC RTC OLED OLED OLED OLED OLED OLED OLED RTC RTC Обозначение вывода на модуле VCC GND GND 3.3V RST DATA CLK D/C CS SCL SDA Ого, сколько соединений! Как мы видели в главе 5, макетная плата позволяет подключать к ней компоненты и использовать перемычки для выполнения соединений. Это упрощает разводку проекта и позволяет перемещать компоненты, если вам нужно освободить больше места. При подключении компонентов всегда следите за тем, чтобы ряды контактов были установлены параллельно центральной выемке или линии. Напомним, что на макетных платах контакты соединены вместе рядами, перпендикулярными центральной линии. Это позволяет вам выполнить более одного подключения к каждому выводу компонента. ВНИМАНИЕ! ном питании! Никогда не подключайте и не отсоединяйте перемычки при включен- Наконец, всегда убедитесь, что вы тщательно выполнили подключения в своем проекте, дважды проверяя все соединения – особенно питание, общий провод и контакты, используемые для передачи сигналов, например те контакты, которые используются для интерфейса SPI. Для этого проекта я установил Pico на левой стороне макетной платы, модуль OLED – посередине выше центральной линии, а модуль RTC – с правой стороны под центральной линией. Обратите внимание, что плата RTC использует другое питание. Плата OLED использует напряжение 3,3 В, а плата RTC – 5 В1. Всегда проверяйте требования к питанию ваших компонентов 1 В таких случаях целесообразно для отличающихся питаний выбрать разные цвета проводов питания: для основного, как обычно, красный, а для дополнительного, например, оранжевый. – Прим. перев.
284  Проект «Hello, World!» в стиле MicroPython перед включением проекта. Перепроверьте и трижды проверьте свои соединения. Если вы выбрали плату RTC, отличную от той, что показана на рисунке, обязательно исправьте соединения при необходимости. Например, на плате часов DS1307 SparkFun (по ссылке в табл. 8.1) контакты расположены в другом порядке, поэтому не руководствуйтесь только приведенным рисунком, особенно если вы используете альтернативные компоненты, – обращайте внимание на обозначения! На рис. 8.3 показана схема соединений для проекта часов MicroPython. Обратите внимание, что на изображении показан Raspberry Pi Pico, но вы можете заменить его на Raspberry Pi Pico W без каких-либо изменений. Рис. 8.3  Соединения проекта часов Pico (на полноразмерном макете) Если у вас нет полноразмерной макетной платы, вы можете использовать две половинного размера и соединить их между собой. Если присмотреться к таким платам, то можно увидеть выступы с одной стороны и соответствующие выемки с другой. ПРИМЕЧАНИЕ Хотя для большинства проектов в этой книге можно использовать макетную плату половинного размера, в данном случае использовать полноразмерную проще. Проект можно уместить и на один макет половинного размера, но разводка будет выглядеть гораздо запутаннее (рис. 8.4). Соединения для Arduino Nano RP2040 Connect Поскольку мы используем те же компоненты для Arduino Nano RP2040 Connect, соединения будут аналогичными, но, как отмечалось в предыдущих главах, Nano RP2040 имеет другое расположение контактов. Таким образом,
Настройка оборудования  285 некоторые соединения будут отличаться (например, интерфейс SPI использует даже другие названия). Если вы используете Nano RP2040 для этого проекта, дважды проверьте соединения, показанные в табл. 8.3, чтобы убедиться, что вы все подключаете правильно. Рис. 8.4  Соединения проекта часов Pico (на макетной плате половинного размера) Таблица 8.3. Соединения для часов (Nano RP2040) Обозначение вывода Nano (название, функция) +5V GND GND +3V3 D8 (GP20) D11 (GP07, MOSI, COPI) D13 (GP06, SCK) D5 (GP17) D4 (GP16) A5 (GP13, SCL) A4 (GP12. SDA) Плата модуля RTC RTC OLED OLED OLED OLED OLED OLED OLED RTC RTC Обозначение вывода на модуле 5V GND GND 3.3V RST DATA CLK D/C CS SCL SDA
286  Проект «Hello, World!» в стиле MicroPython ВНИМАНИЕ! Всегда дважды и трижды проверяйте свои соединения, особенно все соединения питания и заземления. Обязательно проверьте соединения питания, чтобы убедиться, что к компонентам правильно подается питание (3 В или 5 В). Подключение неправильного напряжения может привести к повреждению компонента. На рис. 8.5 показана схема соединений для проекта часов с использованием Arduino Nano RP2040 Connect вместо Raspberry Pi Pico. Поскольку Nano RP2040 короче Pico, на макетной плате половинного размера остается больше места. Рис. 8.5  Соединения проекта часов Nano RP2040 (на макетной плате половинного размера) Подключение контакта 5 В на Arduino Nano RP2040 Connect Arduino Nano RP2040 Connect поставляется с отключенным контактом 5 В. Это предупредительная мера, так как все меньше современных модулей и компонентов используют напряжение 5 В (большинство – 3,3 В). Поскольку в этом проекте и во многих других проектах в данной книге используется напряжение 5 В, нам необходимо подключить этот вывод. Это можно сделать с помощью перемычки, расположенной в нижней части платы, как показано на рис. 8.6.
Написание кода  287 Рис. 8.6  Перемычка для подключения контакта 5 В на плате Nano RP2040 (с разрешения arduino.cc) Место для установки перемычки представляет две контактные площадки для поверхностного монтажа. Вам нужно будет покрыть припоем каждую контактную площадку, а затем соединить контактные площадки между собой1. Если вы не умеете паять, сейчас самое время научиться! Еще раз: всегда проверяйте соединения перед включением платы! Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте плату – потребуется немало обсуждений, прежде чем мы будем готовы протестировать проект. Написание кода Теперь пришло время написать код для нашего проекта. Хотя между контактами GPIO, используемыми для Pico, и Nano RP2040 есть некоторые незначительные различия, код одинаков для обеих плат. Различия в коде выделены в несколько строк, которые будут определены по ходу работы. ПРИМЕЧАНИЕ Если не указано иное, представленный код работает как на Pico, так и на Nano RP 2040. Поскольку мы работаем с несколькими новыми компонентами, я буду представлять код для каждого по очереди. Код не слишком сложен, но может 1 Можно попробовать посадить блямбу припоя между площадками, но надежней, быстрее и менее опасно для сохранности платы будет взять отрезок тонкой медной проволочки, загнуть его кончик под прямым углом и, удерживая за остальную часть пинцетом или тонкими плоскогубцами, припаять настоящую перемычку. Торчащий конец проволочки обязательно следует откусить у самого основания. – Прим. перев.
288  Проект «Hello, World!» в стиле MicroPython быть не таким понятным, как часть кода из предыдущих проектов. Начнем с рассмотрения конструкции проекта. Проектирование После того как вы разобрались с аппаратным обеспечением и определили, как подключить компоненты к плате, пришло время приступить к разработке кода. К счастью, этот проект достаточно мал, чтобы упростить его устройство. Мы хотим отображать время на OLED-дисплее раз в секунду. Таким образом, работа кода заключается в чтении даты и времени из RTC и последующем отображении их на OLED. Ниже перечислены шаги, которые суммируют, как спроектировать и реализовать код для этого проекта или любого другого аналогичного проекта.   Библиотеки: нам нужно будет выбрать и импортировать библиотеки для RTC и OLED.   Настройка: нам нужно будет настроить интерфейсы I2C и SPI.   Инициализация: нам нужно будет инициализировать экземпляры объектов для классов в библиотеках.   Новые функции: написание нескольких вспомогательных функций, чтобы лучше организовать код.   Основной код: создание основного кода проекта.   Тестирование внешних модулей: мы предпримем дополнительный шаг для тестирования каждой внешней платы отдельно, прежде чем пытаться выполнить весь код.   Копирование на плату: назовите файл main.py (или как хотите) и скопируйте его на плату контроллера. Перечисленные шаги мы будем воспроизводить в большинстве проектов в этой книге, и это хороший образец, которому можно следовать во всех ваших проектах MicroPython. Шаг «Новые функции» позволяет нам вынести рабочую часть кода в отдельную функцию, чтобы упростить вызов из основной функции. С подробностями мы познакомимся позднее, в процессе выполнения и тестирования проекта. Теперь, когда мы знаем общий план реализации кода проекта, рассмотрим необходимые библиотеки. Требуемые библиотеки Вспоминая изложенное ранее, мы выясняем, что нужны две библиотеки: одна для OLED-дисплея, а другая для RTC. Библиотеку для OLED-дисплея можно найти по адресу https://github.com/adafruit/micropython-adafruit-ssd1306, а биб­ лиотеку для RTC – по адресу https://github.com/peter-l5/DS1307/tree/main. Загрузите обе библиотеки. У вас должна быть возможность зайти на сайты и щелкнуть ссылку Clone or Download (Клонировать или загрузить), а затем
Написание кода  289 кнопку Download Zip (Загрузить Zip), чтобы загрузить файлы на свой компьютер. Затем откройте папку с загруженными файлами и разархивируйте их. Вы должны найти следующие файлы:   ssd1306.py: библиотека OLED-дисплеев;   ds1307.py: библиотека RTC. Обычно мы создаем новую папку для каждого проекта, который хотим разместить на нашей плате, но поскольку этот проект будет запускаться на плате автоматически при ее включении, файл основного кода должен будет называться main.py и быть помещен в корневую папку (название файла с расширением .py не имеет значения). Мы создадим новую папку для размещения файлов библиотек (драйверов) для внешних модулей. После подключения Pico создайте новую папку с именем project1 в корневой папке, щелкнув правой кнопкой мыши Pico в Thonny и выбрав New directory… (Новый каталог…). Затем дважды щелкните созданную папку project1 и загрузите файлы библиотеки в нее. Вы должны увидеть структуру папок и список файлов, аналогичные рис. 8.7. Рис. 8.7  Новая папка и файлы проекта (Thonny) Теперь, когда у нас есть скопированные библиотеки, давайте посмотрим на код, который нам нужно будет написать. Планирование кода Теперь, когда у нас есть папки проекта, а также загружены (и при необходимости доработаны) библиотеки, мы можем приступить к написанию кода. Вместо того чтобы показывать вам длинный текст всего кода и говорить
290  Проект «Hello, World!» в стиле MicroPython «пойми или погибни», давайте сначала пройдемся по всем частям кода, чтобы понять каждую часть. По мере изучения кода не стесняйтесь тестировать отдельные части самостоятельно, но можете подождать до конца, чтобы протестировать код отдельно. Кое-что из изложенного будет знакомо и, возможно, элементарно для тех, кто уже работал с примерами в этой книге, но немного освежить знания никогда не помешает. Начнем с обзора раздела импорта. Импорт Раздел импорта находится перед всеми остальными операторами в верхней части файла, после блока комментариев. В блоке комментариев в начале файла следует включить некоторое пояснение, чтобы знать, что делает код. Вам не нужно писать длинное руководство – просто короткое заявление, описывающее программу, включая ваше имя, функциональность и другую информацию (например, дату создания). Это важно, если вы хотите поделиться своим кодом с другими или если вы когда-нибудь вернетесь к нему позже для повторного использования. Если хотите вводить код по ходу дела, откройте новый файл с именем main. py на своем ПК с помощью Thonny или вашего любимого редактора кода. Мы скопируем файл в Pico позже. Ниже показан раздел импорта для этого проекта: # Import libraries from project1.ds1307 import DS1307 from project1.ssd1306 import SSD1306_SPI from utime import sleep from machine import SPI, Pin, SoftI2C Обратите внимание, что мы указали project1 в первых двух операторах, поскольку поместили библиотеки (драйверы) в папку project1. Файл main.py будет скопирован в корень файловой системы Pico. Кроме того, мы используем библиотеку SoftI2C вместо I2C, поскольку библиотека ds1307 со встроенной реализацией I2C на Pico не работает (см. пояснения на стр. 215). Настройка Далее нам нужно настроить интерфейсы I2C и SPI для использования в биб­ лиотеках ds1307 и ssd1306. То есть классам в этих библиотеках нужны экземпляры объектов интерфейсов, передаваемых конструктору. Код, который мы будем использовать, похож на код, который мы видели в предыдущих примерах. Ниже показан код настройки интерфейса: # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13)
Написание кода  291 # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # SPI for the OLED # # Arduino Nano RP2040 Connect uses mosi=7, sck=6 #spi = SPI(0, 100000, mosi=Pin(7), sck=Pin(6)) # # Raspberry Pi Pico uses mosi=19, sck=18 spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18)) # Обратите внимание, что мы используем разные параметры для SPI и указываем контакты для I2C. При желании вы можете использовать и другие контакты, но не забывайте использовать правильные контакты при соединении компонентов. Также обратите внимание, что у нас есть комментарии, обозначающие различия между Pico и Nano RP2040. Чтобы использовать правильный код, просто закомментируйте (поставьте # в строке слева от кода) ненужные строки. Для Pico I2C использует контакты 8 и 9 для SDA и SCL, а для SPI Pico применяет 19 и 18 для MOSI и SCK. И наоборот, Nano RP2040 использует контакты 12 и 13 для I2C и 7 и 6 для SPI1. Это единственные изменения, необходимые для запуска проекта на любой плате. Инициализация Далее мы инициализируем экземпляры объектов для классов в библиотеках. Это тот момент, когда вам нужно прочитать документацию для каждой биб­ лиотеки, чтобы понять, что необходимо для инициализации объектов. Для библиотеки ssd1306 конструктор класса требует указания количества пикселей (разрешение – количество пикселей в строках и столбцах) при создании экземпляра интерфейса SPI, а также контактов, которые мы будем использовать для линий D/C, RST и CS. Для библиотеки RTC нам нужно передать только экземпляр интерфейса SoftI2C. Ниже показано, как выполнить оба шага: # Initialize class instance variables for RTC, OLED rtc = DS1307(i2c) # (year, month, day, hours, minutes, seconds, weekday: integer: 0-6) # rtc.datetime = (2023,09,11,16,15,30,2) oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16)) 1 В тексте программы номера контактов соответствуют нумерации GPIO-выводов контроллера RP2040. Соответствие названий контактов маркировке выводов на платах см. в табл. 8.2 и 8.3. – Прим. перев.
292  Проект «Hello, World!» в стиле MicroPython ПРИМЕЧАНИЕ Этот код не является ошибочным. Мы будем использовать одни и те же контакты для D/C, RES и CS как для Pico, так и для Nano RP 20401. Обратите внимание, что здесь есть закомментированные строки. Когда мы впервые используем RTC или заменили резервную батарейку, мы должны инициализировать дату и время. Для этого мы можем использовать возможности библиотеки. В этом случае мы просто вызываем функцию datetime, которая предназначена для приема кортежа (вместо вызова функции с использованием параметров) для экземпляра RTC, содержащего новую дату и время начала. В закомментированной строке показан порядок элементов кортежа, вторая строка запускает установку времени. После установки нам не нужно запускать ее снова. Повторный запуск приведет к сбросу RTC, и нам не нужно этого делать. Таким образом, мы оставляем эту строку закомментированной для нормальной работы и раскомментируем ее, когда нам нужно сбросить RTC. При первом запуске проекта раскомментируйте этот код, указав правильную текущую дату и время, и сразу закомментируйте его снова2. Новые вспомогательные функции Теперь, когда весь код настройки и инициализации определен, мы можем создать несколько вспомогательных функций, которые позволят нам организовать код. Напомним, мы хотим, чтобы проект считывал дату и время из RTC и отображал их на OLED раз в секунду. Таким образом, мы ожидаем увидеть некий цикл, выполняющий эти два шага. Однако мы должны снова обратиться к документации библиотеки, где мы обнаруживаем, что RTC возвращает данные в виде кортежа (год, месяц, день, день недели, час, минута, секунда, миллисекунда). Это означает, что мы должны отформатировать дату и время, чтобы людям было легче их читать на маленьком OLED-экране. Это идеальный кандидат на роль вспомогательной функции. Давайте создадим функцию с именем write_time(), которая принимает экземпляр OLED-дисплея и RTC, затем прочитает дату и время с помощью функции datetime() (без параметров) и выведет их на OLED-экране с помощью функции text(), которая принимает начальный столбец (называемый в документации позицией X) и строку (позицию Y) в качестве места 1 2 Как и ранее, номера в коде соответствуют нумерации GPIO-выводов контроллера RP2040. Соответствие названий контактов маркировке выводов на платах см. в табл. 8.2 и 8.3. – Прим. перев. Реальные RTC-модули со временем начинают отставать (особенно при пребывании в выключенном состоянии при пониженных температурах зимой), поэтому обычно корректировку времени необходимо производить раз в год или в полгода. Хорошие RTC-библиотеки автоматически извлекают время из компьютера в момент компиляции, что избавляет от необходимости формировать строку вручную, стараясь не опоздать к запуску. Попробуйте найти такие библиотеки для MicroPython и контролера RP2040 или самостоятельно доработать имеющиеся. – Прим. перев.
Написание кода  293 на экране для вывода сообщения при вызове функции show(). Размещение в отдельной функции этих действий, составляющих суть проекта, позволяет изолировать эти действия и упростить поддержку или изменение кода – ведь все главное находится в одном месте. # Display the date and time def write_time(oled, rtc): # Get datetime dt = rtc.datetime # Print the date oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]),0, 0) # Print the time oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[3], dt[4], dt[5]),0, 10) # Print the day of the week oled.text("Day: {0}".format(get_weekday(dt[6])), 0, 20) # Update the OLED oled.show() Обратите внимание, что мы используем функцию text() и функцию format() для получения данных из часов реального времени и форматирования их в ожидаемом формате, который использует большинство часов: ЧЧ:ММ::СС и ММ/ДД/ГГГГ1. Обратите внимание, что здесь есть дополнительная функция с именем get_weekday(). Эта функция принимает номер дня недели, возвращенный из RTC, и возвращает строку с названием дня. Ниже показан код этой функции2: # Return a string to print the day of the week def get_weekday(day): if day == 1: return "Sunday" elif day == 2: return "Monday" elif day == 3: return "Tuesday" elif day == 4: return "Wednesday" elif day == 5: return "Thursday" elif day == 6: return "Friday" else: return "Saturday" Добавлена еще одна функция – функция очистки экрана. Эта функция просто очищает экран, чтобы мы могли перезаписать его новыми данными. 1 2 Обратите внимание, что дата здесь воспроизводится в американском формате (ММ/ДД/ГГГГ), где месяц идет впереди номера дня, для европейских/российских пользователей ее следует отформатировать в привычном виде ДД.ММ.ГГГГ. – Прим. перев. В США (и Канаде) нумерация дней недели начинается с воскресенья (Sunday, 1-й день). Для Европы/России первым днем является понедельник (Monday), воскресенье – последний (номер 7). Соответственно, следует откорректировать код этой функции в библиотеке, перенумеровав названия (дни недели по названиям по всему миру одинаковы, а их номера отличаются в зависимости от региона). При этом в строке задания времени при инициализации часов (см. раздел «Инициализация» выше) следует указывать новое перенумерованное значение – микросхема RTC должна понять вас правильно. – Прим. перев.
294  Проект «Hello, World!» в стиле MicroPython Обычно в этом нет необходимости, но рекомендуется очистить экран на случай, если библиотека не сделает этого за вас1. Эта функция называется clear_screen(), и ее код показан ниже. В нем просто используются функции fill() и show() из библиотеки ssd1306. Передача 0 для функции fill() указывает библиотеке заполнить экран без данных (пустое место). # Clear the screen def clear_screen(oled): oled.fill(0) oled.show() Основной код Теперь мы готовы написать новую функцию main() для проекта. У нас разработаны вспомогательные функции, поэтому нам нужно только вызывать их и ждать секунду на каждом проходе. Мы сделаем так, чтобы при выполнении скрипта вызывалась функция main() и перехватывалось действие по остановке выполнения. Делается это с помощью следующего кода: if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!") sys.exit(0) Мы используем блок try...except, чтобы перехватить прерывание клавиатуры (<CTRL>+<C>) и остановить выполнение. Эта конструкция типична для написания подобных сценариев, предназначенных для непрерывного выполнения. Напомним, когда скрипт Python загружается (читается), выполняется каждая строка кода последовательно. Если мы поместим весь наш код в функции, нам понадобится какой-то способ начать контролируемое выполнение. Приведенный выше код выполняет эту задачу, и мы будем использовать его во всех наших проектах. Не имеет значения, как вы называете функцию для основного кода, но название main – это обычная практика. Далее мы можем создать функцию main(). Ниже показана функция с кодом настройки и инициализации, обсуждавшимся ранее: def main(): # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 1 На самом деле потому, что заполнение строки на OLED-экране происходит посимвольно, и если какие-то места в новом выводе останутся незаполненными, в строке на экране окажутся лишние символы. Надо только учитывать, что очистка всего экрана может занимать больше времени, чем посимвольное заполнение, потому при ежесекундной очистке возможно неприятное мерцание экрана. – Прим. перев.
Написание кода  295 sda = Pin(12) scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 #sda = Pin(8) #scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # SPI for the OLED # # Arduino Nano RP2040 Connect uses mosi=7, sck=6 spi = SPI(0, 100000, mosi=Pin(7), sck=Pin(6)) # # Raspberry Pi Pico uses mosi=19, sck=18 #spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18)) # # Initialize class instance variables for RTC, OLED rtc = DS1307(i2c) # (year, month, day, hours, minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,11,16,15,30,2) oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16)) for i in range(10): clear_screen(oled) write_time(oled, rtc) sleep(1) Обратите внимание, насколько краткая эта функция – мы видим только три оператора: очистить экран, показать время и подождать одну секунду. Давайте объединим этот код с разделом импорта и вспомогательными функциями. В лис­тин­ге 8.1 показан полный код файла main.py. Листинг 8.1  Полный код сценария часов MicroPython (файл main.py)1 # # MicroPython for the IoT Second Edition - Chapter 8 # # Chapter 08 - Digital Timepiece # # This example uses an I2C real time clock (RTC) to store the current # date and time. It displays the time, date, and day of the week on # an SPI OLED. # # Dr. Charles Bell # # Import libraries from project1.ds1307 import DS1307 1 Обратите внимание, что код откорректирован в расчете на выполнение на плате Arduino Nano RP2040; строки для Pico закомментированы (в отличие от тестирующих программ далее). – Прим. перев.
296  Проект «Hello, World!» в стиле MicroPython from project1.ssd1306 import SSD1306_SPI from utime import sleep from machine import SPI, Pin, SoftI2C # Return a string to print the day of the week def get_weekday(day): if day == 1: return "Sunday" elif day == 2: return "Monday" elif day == 3: return "Tuesday" elif day == 4: return "Wednesday" elif day == 5: return "Thursday" elif day == 6: return "Friday" else: return "Saturday" # Display the date and time def write_time(oled, rtc): # Get datetime dt = rtc.datetime # Print the date oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0) # Print the time oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[3], dt[4], dt[5]), 0, 10) # Print the day of the week oled.text("Day: {0}".format(get_weekday(dt[6])), 0, 20) # Update the OLED oled.show() # Clear the screen def clear_screen(oled): oled.fill(0) oled.show() def main(): # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 sda = Pin(12) scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 #sda = Pin(8) #scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # SPI for the OLED # # Arduino Nano RP2040 Connect uses mosi=7, sck=6 spi = SPI(0, 100000, mosi=Pin(7), sck=Pin(6)) # # Raspberry Pi Pico uses mosi=19, sck=18 #spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
Написание кода  297 # # Initialize class instance variables for RTC, OLED rtc = DS1307(i2c) # (year, month, day, hours, minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,11,16,15,30,2) oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16)) for i in range(10): clear_screen(oled) write_time(oled, rtc) sleep(1) if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!") sys.exit(0) Уделите некоторое время прочтению кода, чтобы увидеть, как он организован. Мы будем использовать этот код как шаблон в будущих проектах. Вы можете сохранить его на свой компьютер, но пока не запускайте его на Pico, потому что необходимо еще протестировать код для внешних модулей. Тестирование внешних модулей Теперь, когда мы спланировали код и знаем, как закодировать каждую из частей, нам осталось сделать еще одну вещь – протестировать платы внешних модулей по отдельности1. Мы делаем это, подключая одну плату и проверяя ее, затем отключая и отсоединяя эту плату, подключая другую плату и проверяя ее. Это хорошая практика, чтобы выработать привычку делать это по одной основной причине. Вы сэкономите себе много времени, тестируя отдельные части проекта, особенно аппаратное обеспечение, по очереди. Это не только облегчает решение любых проблем, но и гарантирует, что вы сможете определить источник проблемы. То есть если вы подключили все оборудование, все подключили, написали код, развернули, затем включили и ничего не работает, как узнать, какая часть виновата? Это одна из моих мантр: создавайте и тестируйте по одному компоненту за раз. 1 В реальном проекте, конечно, следует выполнить этот этап до того, как начинать писать полный код, – на одном из начальных этапов, с тем чтобы не пришлось «лопатить» готовую программу в спешке, если вдруг обнаружится какая-то несовместимость (как у автора с оригинальной библиотекой I2C и интерфейсом чипа DS1307) и придется срочно подыскивать альтернативные решения. Тестирование модулей (и вообще любых компонентов схемы) по отдельности делается именно для обнаружения всяких несуразностей; для того чтобы к моменту составления полного кода уже иметь отлаженные фрагменты для каждого компонента. – Прим. перев.
298  Проект «Hello, World!» в стиле MicroPython СОВЕТ Тестирование кода по частям – это мне знакомая схема, и я настоятельно рекомендую вам освоить данный процесс самостоятельно. В этом проекте есть два внешних модуля – RTC и OLED. Давайте посмот­ рим, как протестировать их по отдельности. Представленный код предназначен для запуска через консоль REPL через Thonny. Проверка модуля RTC Чтобы проверить RTC, используйте код, показанный в лис­тин­ге 8.2. Он представляет собой сокращенную форму только минимально необходимой части кода, представленного в лис­тин­ге 8.1. Вы можете назвать этот файл test_rtc.py, если хотите его сохранить, но мы будем выполнять код через консоль REPL. Заметьте, что мы включили код только для платы Pico, поэтому обязательно раскомментируйте строки для платы Arduino Nano RP2040, если вы ее используете, и закомментируйте для Pico. Листинг 8.2  Тестовый код для модуля RTC (test_rtc.py) # # MicroPython for the IoT Second Edition - Chapter 8 # # Chapter 08 - Digital Timepiece # from project1.ds1307 import DS1307 from utime import sleep from machine import Pin, SoftI2C # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) rtc = DS1307(i2c) # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) rtc.datetime = (2023,09,11,16,15,30,2) for i in range(0,10): # Get datetime dt = rtc.datetime print("\nTest:", i+1) # Print the date print("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0])) # Print the time print("Time: {0:02}:{1:02}:{2:02}".format(dt[3], dt[4], dt[5])) sleep(3)
Написание кода  299 Обратите внимание, что мы установили в часах дату и время для этого теста. Перед запуском данного примера у себя следует изменить кортеж даты и времени, чтобы включить текущую дату и время при запуске теста. В консоли REPL вы должны увидеть вывод кортежа, представляющего дату и время. Они должны быть такими же, как вы устанавливали, поскольку код будет запускаться гораздо меньше секунды с момента его установки до момента запроса RTC. После каждой итерации время должно увеличиваться на три секунды (последний оператор sleep(3)). В лис­тин­ге 8.3 показано, как должен выглядеть результат успешного теста. Листинг 8.3  Проверка RTC Test: 1 Date: 09/11/2023 Time: 16:15:30 Test: 2 Date: 09/11/2023 Time: 16:15:33 Test: 3 Date: 09/11/2023 Time: 16:15:36 Test: 4 Date: 09/11/2023 Time: 16:15:39 Test: 5 Date: 09/11/2023 Time: 16:15:42 Test: 6 Date: 09/11/2023 Time: 16:15:45 Test: 7 Date: 09/11/2023 Time: 16:15:48 Test: 8 Date: 09/11/2023 Time: 16:15:51 Test: 9 Date: 09/11/2023 Time: 16:15:54 Test: 10 Date: 09/11/2023 Time: 16:15:57 Если какое-либо из предположений по выводу не соответствует действительности, обязательно проверьте соединения и найдите ошибки. Также убе-
300  Проект «Hello, World!» в стиле MicroPython дитесь, что вы используете правильную модифицированную версию биб­ лиотек (и что вы скопировали их на плату в нужную папку). Проверка модуля OLED Чтобы протестировать OLED, используйте код из листинга 8.4. Он представляет собой сокращенную форму части кода, который мы видели в лис­тин­ ге 8.1, с добавлением только минимально необходимого. Вы можете назвать этот файл test_oled.py, если хотите его сохранить, но мы будем выполнять код через консоль REPL. Заметьте, что мы включили код только для платы Pico, поэтому обязательно раскомментируйте строки для платы Arduino Nano RP2040, если вы ее используете, и закомментируйте для Pico. Листинг 8.4  Test Code for the OLED Breakout Board (test_oled.py) # # MicroPython for the IoT Second Edition - Chapter 8 # # Chapter 08 - Digital Timepiece # from project1.ssd1306 import SSD1306_SPI from machine import Pin, SPI from utime import sleep_ms # # SPI for the OLED # # Arduino Nano RP2040 Connect uses mosi=7, sck=6 #spi = SPI(0, 100000, mosi=Pin(7), sck=Pin(6)) # # Raspberry Pi Pico uses mosi=18, sck=18 spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18)) # oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16)) for i in range(40): for j in range(32): oled.fill(0) oled.show() oled.text("HELLO WORLD",i,j) oled.show() sleep_ms(100) Когда вы запустите этот код, вы должны увидеть пустой экран (он должен быть пустым с самого начала), затем отобразить приветственное сообщение в различных местах OLED-экрана и, возможно, «прокрутить» экран (можете ли вы заметить почему?). Если вы не видите никаких надписей, выключите питание и проверьте все соединения, использование правильных выводов и что на вашу плату скопирована правильная модифицированная версия библиотеки.
Выполнение кода  301 СОВЕТ Панели OLED на платах от Adafruit (и предположительно других производителей) поставляются с защитной пленкой над экраном. Вы можете оставить ее на месте, чтобы не повредить экран. OLED-экран достаточно яркий, чтобы видеть сквозь защитную пленку. Отлично, теперь мы готовы выполнить и протестировать завершенный проект. Выполнение кода Наконец, мы подошли к моменту, когда можем скопировать все файлы на плату и выполнить код проекта целиком. В этом процессе рекомендуется следовать некоторым этапам, показанным ниже. Вам следует следовать этому порядку каждый раз, когда вы хотите развернуть и протестировать проект. 1. Дважды проверьте все аппаратные соединения (перемычки). 2. Подключите Pico к компьютеру. 3. Скопируйте библиотеки и файл кода на плату. 4. Проверьте код, исправьте все обнаруженные проблемы и при необходимости повторно скопируйте файлы1. 5. Отключите и снова подключите плату. Первый шаг невозможно переоценить. Всегда проверяйте соединения проводов каждый раз перед включением платы. Это на тот случай, если мимо вашего проекта бродили любопытные руки и «осматривали» его, или вы его перемещали, или произошло какое-то другое событие, приведшее к отключению соединений. Быть особенно осторожным никогда не помешает. Затем мы подключаем плату к ПК, чтобы включить плату и проверить наличие проблем. Да, это тест на задымление! Просто убедитесь, что все светодиоды, которые должны светиться (например, на плате), горят, а те, которые не должны гореть, выключены. Например, если вы видите сплошную полосу на OLED-дисплее при включении, это плохой знак. Если у вас возникнут сомнения, отключите Pico и проверьте соединения. Если что-то по-прежнему не так, отключите все и проверьте свою плату. Иногда поврежденный компонент может вызывать странное поведение. Далее мы копируем на плату все библиотеки и код, которые хотим использовать. Напомним, мы копируем библиотеки для RTC и OLED в папку на плате с именем project1 и копируем файл main.py в корневую папку на плате. 1 В частности, убедитесь, что раскомментированы именно строки инициализации для той платы, которую вы используете, а для второй оставлены закоментированными. Напомним, что в тексте по листингу 8.1 (main.py) закоментированны строки для Pico, в расчете на выполнение на Arduino Nano RP2040 (см. рис. 8.8 далее), в тестовых программах (test_oled.py и test_rtc.py) – наоборот. – Прим. перев.
302  Проект «Hello, World!» в стиле MicroPython ВНИМАНИЕ! Обязательно оставьте раскомментированными строки для загрузки даты/времени в RTC при первом выполнении. Вы должны закомментировать их сразу после первого теста! На данный момент код не выполняется, но мы можем выполнить его через Thonny. Просто нажмите кнопку Run (Выполнить) и наблюдайте за выполнением вашего кода! Если все подключено правильно и код верен, вы увидите дату и время на OLED-дисплее. Вы должны увидеть что-то вроде рис. 8.8, на котором показан проект во всей его красе. Рис. 8.8  Часы на MicroPython! Если что-то не работает, вернитесь и проверьте свой код. Если вы оставили две строки кода для инициализации RTC без комментариев, то можете увидеть, что одна и та же дата и время появляются при каждом запуске кода. Обязательно закомментируйте их при последующих выполнениях. Или, если вы забыли раскомментировать эти строки, вы можете увидеть странные значения даты и времени. Именно на этом этапе вы должны насладиться чудом своего первого успешного аппаратного проекта MicroPython. Найдите время, чтобы насладиться радостью от хорошо выполненной работы! Однако мы еще не закончили. Есть еще один шаг. Отключите Pico и выйди­ те из Thonny, затем снова подключите его к компьютеру, чтобы опять включить его. Если правильные дата и время появятся через несколько секунд, значит, вы это сделали! Вы успешно создали проект, который можно упаковать в корпус и запустить где угодно, и пока резервная батарейка заряжена, он не будет терять время.
А что дальше  303 А что дальше? Этот проект имеет большой потенциал для совершенствования. Если вам понравился проект, вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые вы можете рассмотреть. Некоторые из них просты, другие могут оказаться сложной задачей:   используйте другой тип RTC;   рассчитайте AM/PM и отобразите его1;   используйте дисплей большего размера и отображайте дату со словесным наименованием месяца;   используйте датчик освещенности, чтобы выключать дисплей под прямыми солнечными лучами;   добавьте динамик и реализуйте функцию будильника (подсказка: большинство типов RTC имеют эту функцию);   отформатируйте дату и время, используя различные мировые стандарты, такие как ГГГГ/ММ/ДД. Конечно, если вы хотите перейти к следующему проекту, вы можете это сделать, но потратьте некоторое время на изучение этих потенциальных доработок – это будет хорошей практикой2. Если вы считаете, что данный проект совершенно отдельный и в дальнейшем нам не пригодится, подумайте вот о чем: большинство проектов созданы на основе датчиков, и даже большинство проектов, которые генерируют данные, должны быть связаны с датой и временем выборки событий. Таким образом, использование RTC для чтения даты и времени будет рассматриваться во многих IoT-проектах. Создание собственного модуля RTC Если вы похожи на меня и любите повозиться, вы можете создать свой собственный модуль RTC, используя микросхему DS1307, два резистора, кварцевый резонатор и гнездо для подключения батарейки типа «монетка». Эти компоненты можно найти в большинстве интернет-магазинов электроники, таких как Adafruit (www. adafruit.com), SparkFun (www.sparkfun.com) и Mouser (www.mouser.com). Список компонентов следующий: • чип DS1307; 1 2 Выдавать время в 12-часовом режиме (AM – «до полудня» и PM – «после полудня») может и сама микросхема RTC (любого типа!), это зависит от начальных настроек. Большинство любительских библиотек не имеют возможности такой настройки, потому их необходимо дорабатывать, руководствуясь документацией к используе­ мому типу RTC. – Прим. перев. Для отечественного читателя к перечню выше стоит добавить изучение возможности отображения на выбранном типе дисплея русских наименований дней недели и месяцев. – Прим. перев.
304  Проект «Hello, World!» в стиле MicroPython • • • • гнездо для батарейки типа «монетка»1; батарейка-«монетка» 3 В; кварцевый резонатор 32 768 Гц; резистор 1 кОм – 2 шт. Ниже показано, как соединить компоненты на макетной плате. См. https://www.instructables.com/Arduino-Real-Time-Clock-DS1307, где приведен пример сборки этого дополнительного проекта. Если вы планируете реализовать множество проектов, использующих RTC, покупка этих компонентов оптом и подключение собственного модуля RTC DS-1307 могут оказаться более экономически выгодными. Кроме того, это повышает надежность вашего комплекта2. 1 2 Как и для основного проекта, рекомендуется выбирать батарейку типа CR2032 и соответствующее гнездо. Гнезда под такие батарейки продаются практически в каждом интернет-магазине электроники, сами батарейки также можно приобрести в любых салонах связи или супермаркетах. Рекомендуемый автором тип CR1225 или CR1220 встречается гораздо реже, а гнезда для них тем более придется поискать. – Прим. перев. Надежность повышается потому, что вы имеете возможность выбрать качественный кварцевый резонатор (рекомендуется точность начальной частоты не хуже ±10 ppm). Случайные кварцевые резонаторы, которыми комплектуются фабричные модули, могут приводить к уходу часов до единиц минут в месяц, что для электронных часов неприемлемо высокая величина (хуже всего в этом отношении чип DS3231 со встроенным резонатором, он может через полгода-год пребывания в выключенном состоянии вообще отказать «держать» время от резервного источника). Причем практика показывает, что от выбора поставщика модуля здесь мало что зависит.
Итоги  305 Итоги Работа с оборудованием, таким как платы внешних модулей и библиотеки, необходимые для взаимодействия с ними через специализированные интерфейсы, такие как I2C и SPI, может оказаться сложной задачей. Иногда, как мы видели в этой главе, вам нужна программная версия библиотек SPI или I2C. Причиной этого является растущее количество плат, производители которых создают специализированные версии прошивки MicroPython, которые могут не работать на 100 % с Pico и Nano RP2040. Хитрость заключается в том, чтобы понять, почему изменения необходимы, и найти время, чтобы внести изменения самостоятельно. Так легко просто сдаться, когда что-то не работает, – не делайте этого! Не торопитесь и поймите проблему, а затем решите ее систематически. В этой главе мы увидели подробное описание часов MicroPython. Мы использовали OLED-дисплей для отображения времени, которое мы считываем с чипа RTC. Попутно мы научились планировать наши проекты, подключать оборудование и писать код для использования при развертывании на нашем Pico. В следующей главе мы рассмотрим проект, в котором используется более низкоуровневое оборудование в виде дискретных компонентов, таких как светодиоды, резисторы и кнопки. Это строительные блоки, которые вам понадобятся для формирования более сложных решений.
Глава 9 Проект: пешеходный переход Теперь, когда у нас есть руководство по проектированию, подключению и реализации проекта MicroPython, давайте рассмотрим более продвинутый проект. В данном случае мы будем использовать некоторые самые базовые компоненты, чтобы дальше учиться работать с оборудованием. Аппаратным обеспечением для этого проекта будут светодиоды, резисторы и кнопки. Кнопка – это самый простой из датчиков. Когда кнопка нажата, мы можем заставить код MicroPython реагировать на это событие. Работа со светодиодами, пожалуй, еще больше похожа на программу «Hello, World!» в стиле проекта для аппаратного обеспечения, потому что включать и выключать светодиоды легко, и, за исключением выяснения того, какой необходим токоограничивающий резистор, подключение светодиодов также просто. Однако мы сделаем задачу более интересной и сложной. Мы реализуем модель светофора и кнопку пешеходного перехода. Кнопка перехода – это кнопка, которую пешеходы могут использовать для включения сигнала светофора, останавливая движение, чтобы они могли перейти улицу. Проекты моделирования могут быть очень интересными, потому что у нас уже есть представление о том, как это должно работать, и мы моделируем то, с чем столкнулись. Например, если вы живете в городе, вы, скорее всего, встречали на перекрестке светофор со знаками «идти/стоять» и с кнопкой. Когда пешеход (или велосипедист) нажимает кнопку, светофоры загораются красным светом, и загорается знак, разрешающий переход. Через некоторое время (30 секунд или около того) знак начинает мигать, а затем, примерно еще через 15 секунд, сигнал разрешения для пешехода отключается, и сигналы светофора возобновляют свой обычный цикл. ПРИМЕЧАНИЕ Здесь слово «цикл» относится к набору состояний, действие которых продолжается одно за другим. Таким образом, цикл относится к смене одного состояния на другое с периодическим возвратом к начальному состоянию.
Необходимые компоненты  307 Обзор В этой главе мы реализуем сигнал светофора с кнопкой пешеходного перехода. Этот проект работает со светодиодами, что позволяет нам видеть состояние нашего кода во время его выполнения. Для светофора мы будем использовать красные, желтые и зеленые светодиоды, чтобы они соответствовали огням светофора того же цвета. Мы также будем использовать красные и зеленые светодиоды пешеходного светофора, соответствующие огням «Стоп» (красный) и «Идти» (зеленый). Мы будем использовать нажимную кнопку, замыкающую цепь только при нажатии. При отпускании она больше не замыкает (выключается). Мы смоделируем светофор и сигнал пешехода, сначала включив только зеленый светодиод светофора и красный сигнал светодиода пешехода. Это нормальное состояние, которое мы будем использовать. При нажатии кнопки светофор на несколько секунд загорается желтым, а затем красным. В это время сигнал пешехода станет зеленым и через несколько секунд начнет мигать. Еще через несколько секунд сигнал пешехода снова загорится красным, а светофор – зеленым. Теперь посмотрим, какие компоненты нужны для этого проекта, затем обсудим, как все соединить. Необходимые компоненты В табл. 9.1 перечислены компоненты, которые вам понадобятся помимо платы MicroPython и USB-кабеля. В последней колонке предоставляются ссылки на поставщиков для ознакомления. Таблица 9.1. Необходимые компоненты Компонент Кол-во Ссылки Красный светодиод 2 www.sparkfun.com/products/9590 Желтый светодиод 1 www.sparkfun.com/products/9594 Зеленый светодиод 2 www.sparkfun.com/products/9592 Резисторы 220 или 330 Ом 5 www.adafruit.com/product/2780 Тактовая кнопка 1 www.sparkfun.com/products/9190 Макетная плата (полноразмерная) 1 www.sparkfun.com/products/12615 www.adafruit.com/product/239 Перемычки «штырь/штырь» 11 www.adafruit.com/product/1950 Вы можете приобрести компоненты отдельно в Adafruit (adafruit.com), SparkFun (sparkfun.com) или в любом магазине электроники, где продаются электронные компоненты.
308  Проект: пешеходный переход Некоторые компоненты, такие как светодиоды и кнопки, можно найти в стартовом комплекте электроники, например в наборе Adafruit Parts Pal, который мы упоминали в главе 2. Покупка основных компонентов, таких как светодиоды, кнопки и резисторы, обходится дешевле, если покупать их в комплекте. Аналогично, подобрать комплект резисторов разных номиналов можно гораздо дешевле, чем если бы вы покупали несколько штук за раз. Фактически вы, скорее всего, обнаружите, что покупка небольшого набора по пять или десять резисторов каждого номинала, которые вам в конечном итоге понадобятся, будет намного дороже, чем если бы вы купили набор1. Вспомним из главы 7, что для светодиодов требуется токоограничивающий резистор, который снижает ток до номинального уровня светодиода. Напомним, что для определения, какой номинал резистора нам нужен, нужно знать несколько параметров светодиода. Необходимые данные включают прямое падение напряжения на светодиоде, напряжение питания (сколько вольт поступает на светодиод) и номинальный ток светодиода. Например, если у меня есть красный светодиод диаметром 5 мм, на вебсайте поставщика (www.sparkfun.com/products/9590) мы находим, что светодиод работает при напряжении 1,8–2,2 В и номинальном токе 20 мА. Допустим, мы хотим использовать это с напряжением питания 5 В. Мы можем взять эти значения и подставить их в формулу: R = (Vcc – Vf )/I. В результате получим сопротивление 150 Ом (подробности см. в главе 7). Это минимальное значение (соответствует номинальному току). Но лучше использовать резисторы побольше – 220 или даже 330 Ом. В результате светодиоды не будут такими яркими, но использовать резистор с большим номиналом безопаснее, чем использовать чересчур маленький. Слишком большой ток – и светодиод перегорит2. Теперь давайте посмотрим, как соединить компоненты вместе. Настройка оборудования Прежде чем приступить к необходимым соединениям, давайте рассмотрим несколько советов по подключению компонентов. Лучший способ подклю1 2 Отечественные магазины вообще, как правило, не продают резисторы поштучно (надо ехать в офлайновый салон, и это действительно обойдется дороже, чем пакетом). Наборы резисторов разных номиналов, о которых говорит автор, можно приобрести, например, в магазине iArduino.ru и некоторых других. – Прим. перев. Автор совершенно прав в этом утверждении, но для большинства светодиодов в корпусе 5 мм 20 мА – номинальный (нормальный рабочий) ток, а не максимально допустимое значение, которое обычно составляет 25–30 мА. Тем не менее мы уже отмечали (см. сноску 2 на стр. 246), что все сигнальные светодиоды нормально светятся при токе 7–10 мА, потому рекомендация автора по выбору номинала совершенно правильная. – Прим. перев.
Настройка оборудования  309 чить компоненты – использовать макетную плату. Как мы видели в главе 2, макетная плата позволяет нам подключать наши компоненты и использовать перемычки для выполнения соединений. В этом проекте мы будем использовать перемычки для питания и общего провода от платы Pico к шинам питания и общего провода макетной платы (те, которые проходят сверху и снизу, отмеченные красной линией для питания и синей или черной для общего провода), а также перемычки на макетной плате для подключения к кнопке. Шину общего провода на одной стороне макетной платы мы будем также использовать для подключения отрицательного вывода всех светодиодов. Кнопка работает в любом положении, если выводы ориентированы с каждой стороны центральной канавки макетной платы. Если расположить кнопку так, чтобы ножки доходили до контактов с любой стороны канавки, она будет ориентирована правильно. Если повернуть ее на 90 градусов, то кнопка либо не будет работать, либо будет всегда замкнута. Если у вас есть сомнения, проверьте выводы кнопки с помощью мультиметра. Вы должны обнаружить, что соединения не замкнуты, когда они не нажаты, и замкнуты, когда нажаты. Единственный здесь полярный компонент – это светодиод (у него есть положительный и отрицательный выводы). Когда вы посмотрите на светодиод, вы увидите, что один вывод светодиода длиннее другого. Этот длинный вывод является положительным1. Мы подключим светодиоды так, чтобы отрицательный вывод был подключен к шине общего провода, а положительный вывод – к основной части макетной платы. Затем мы через центральную канавку подключаем резисторы, соединяющие светодиод с контактом GPIO на плате. Не имеет значения, в каком направлении вы подключите резис­ тор – они будут работать в обоих направлениях. Мы рассмотрим, как подключить Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как вы увидите, соединения аналогичны, но места подключения проводов к платам различаются. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 9.2 показаны соединения, необходимые для этого проекта. Традиционно мы используем как минимум черный цвет для общего провода (отрицательный вывод питания) и красный для положительного вывода питания, но вы можете использовать провода любого цвета по вашему желанию. Мы начнем с физического контакта 40 и дойдем до самого низкого номера используемого контакта. Как вы видите на рис. 3.3, это соответствует обходу по часовой стрелке. 1 Напомним также (см. главу 7), что отрицательный вывод светодиодов в круглых корпусах помечен скосом на бортике корпуса. – Прим. перев.
310  Проект: пешеходный переход Таблица 9.2. Соединения для моделирования пешеходного перехода (Pico) Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на макетной плате 40 VBUS Питание макета (внизу) 38 GND Общий макетной платы (вверху) 17 GP13 Резистор для красного светодиода (светофор) 16 GP12 Резистор для желтого светодиода (светофор) 15 GP11 Резистор для зеленого светодиода (светофор) 12 GP9 Сторона кнопки B 11 GP8 Резистор для красного светодиода (пешеход) 10 GP7 Резистор для зеленого светодиода (пешеход) – Питание макетной Сторона кнопки A платы (внизу) – Общий макетной платы (вверху) Все отрицательные выводы светодиодов – Резисторы Все положительные выводы светодиодов Как мы видели в главе 2, макетная плата позволяет нам подключать компоненты и использовать перемычки для выполнения соединений. Это упрощает проводку проекта и позволяет перемещать компоненты, если вам нужно освободить больше места. На рис. 9.1 показана схема соединений для проекта Pico. Рис. 9.1  Проект модели пешеходного перехода (Pico) Соединения для Arduino Nano RP2040 Connect Поскольку мы используем те же компоненты для Arduino Nano RP2040 Connect, соединения будут аналогичными, но, как отмечалось в предыдущих
Настройка оборудования  311 главах, Nano RP2040 имеет другое расположение контактов. Таким образом, некоторые связи будут отличаться. В табл. 9.3 показаны соединения, необходимые для этого проекта. Таблица 9.3. Соединения для моделирования пешеходного перехода (Nano RP2040) Обозначение на плате Nano RP2040 Соединение (номер GPIO / функция) 5V Питание макетной платы GND Общий макетной платы A5 (GP13) Резистор для красного светодиода (светофор) A4 (GP12) Резистор для желтого светодиода (светофор) A3 (GP29) Резистор для зеленого светодиода (светофор) D2 (GP25) Сторона кнопки B A1 (GP27) Резистор для красного светодиода (пешеход) A0 (GP26) Резистор для зеленого светодиода (пешеход) Питание макетной платы Сторона кнопки A Общий макетной платы Все отрицательные выводы светодиодов Резисторы Все положительные выводы светодиодов На рис. 9.2 показана схема соединений для проекта Nano RP2040. Рис. 9.2  Проект модели пешеходного перехода (Nano RP2040) ВНИМАНИЕ! Повторим еще раз: никогда не подключайте и не отсоединяйте перемычки, когда включено питание. Вы рискуете повредить плату или компоненты. Еще раз: всегда проверяйте соединения перед включением платы. Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте
312  Проект: пешеходный переход плату – необходимо немало обсуждений, прежде чем мы будем готовы протестировать проект. Написание кода Теперь пришло время написать код для нашего проекта. Код не слишком сложен, но немного длиннее, чем предыдущие примеры. Мы увидим, как написать код для имитации кнопки пешеходного перехода и светофора. Нам нужно будет отслеживать состояние кнопки и при ее нажатии включать светодиод, как описано ранее. При инициализации все светодиоды изначально необходимо выключить. Мы напишем функции для мониторинга кнопки и переключения светодиодов и будем использовать прерывание для связи функции кнопки с аппаратным событием, чтобы избежать использования цикла опроса. Импорт Для раздела импорта проекта потребуются класс Pin из библиотеки machine и библиотека time. Ниже показан раздел импорта для проекта: from machine import Pin import time Настройки Код настроек для этого проекта должен будет инициализировать выводы для кнопки и светодиодов, затем выключить все светодиоды (в качестве меры предосторожности), включить зеленый светодиод светофора и красный светодиод пешехода. В лис­тин­ге 9.1 показан код настроек и инициализации. Обратите внимание, что у нас есть два набора контактов: один для Raspberry Pi Pico, а другой для Arduino Nano RP2040 Connect. Обязательно раскомментируйте тот, который соответствует вашей плате1. Листинг 9.1  Настройка и инициализация кнопки и светодиодов # Pins for the Arduino Nano RP2040 Connect #stoplight_red = Pin(13, Pin.OUT) #stoplight_yellow = Pin(12, Pin.OUT) #stoplight_green = Pin(29, Pin.OUT) 1 В тексте программы закомментированы строки для Nano RP2040 и раскомментированы для Pico. – Прим. перев.
Написание кода  313 #button = Pin(25, Pin.IN, Pin.PULL_DOWN) #pedestrian_red = Pin(27, Pin.OUT) #pedestrian_green = Pin(26, Pin.OUT) # Pins for the Pico stoplight_red = Pin(13, Pin.OUT) stoplight_yellow = Pin(12, Pin.OUT) stoplight_green = Pin(11, Pin.OUT) button = Pin(9, Pin.IN, Pin.PULL_DOWN) pedestrian_red = Pin(8, Pin.OUT) pedestrian_green = Pin(7, Pin.OUT) # Setup lists for the LEDs stoplight = [stoplight_red, stoplight_yellow, stoplight_green] pedestrian_signal = [pedestrian_red, pedestrian_green] # Turn off the LEDs for led in stoplight: led.off() for led in pedestrian_signal: led.off() # Start with green stoplight and red pedestrian_signal stoplight[2].on() pedestrian_signal[0].on() Следует обратить внимание на то, как инициализируется кнопка. Вывод, к которому она подключена, – экземпляр объекта Pin, который настроен на вход (чтение) и куда подключен заземляющий резистор. Это позволяет плате определять, когда кнопка нажата, поскольку значение контакта будет положительным, когда соединение установлено (кнопка нажата). Обратите также внимание, что для подключения кнопки в Arduino Nano RP2040 мы используем вывод GPIO25. Это связано с тем, что нам нужен цифровой вывод, а все остальные контакты на стороне подключений светодиодов являются аналоговыми1. Обратите также внимание, что я создаю список, содержащий светодио­ ды светофора (stoplight) и пешеходного сигнала (pedestrian_signal). Это в основном для демонстрации, чтобы вы могли увидеть, как управлять списками объектов классов. Как видите, стало проще вызывать одну и ту же функцию для всех объектов в списке с помощью цикла. Возьмите на заметку данный прием, поскольку он время от времени понадобится вам в других проектах. 1 Как в большинстве контроллеров, аналоговые выводы RP2040 по умолчанию (когда не включены аналоговые функции) работают точно так же, как цифровые, включая реакцию на внешнее прерывание (кроме дополнительных выводов A6 и A7, см. раздел спецификаций по адресу https://docs.arduino.cc/hardware/nano-rp2040-connect). Потому в данном случае можно было бы задействовать любой вывод, например оставшийся свободным A2 (GP28). – Прим. перев.
314  Проект: пешеходный переход Функции Для этой части проекта необходимы две функции. Во-первых, нам нужна функция для циклического переключения светодиодов. Во-вторых, нам нужна функция для контроля нажатия кнопки. Давайте посмотрим на функцию для светодиодов. Мы назовем функцию включения светодиодов по кнопке cycle_lights(). Мы вызываем эту функцию, когда хотим имитировать изменение светофора при нажатии кнопки запроса перехода. Таким образом, эта функция будет вызываться из кода контроля кнопки. В лис­тин­ге 9.2 показана функция cycle_ lights(). Как видите, код довольно простой. Единственной сложной частью может быть цикл, используемый для мигания зеленого светодиода перехода. Обязательно изучите его, чтобы понять, как это работает. Листинг 9.2  Функция cycle_lights() # We need a method to cycle the stoplight and pedestrian_signal # # We toggle from green to yellow for 2 seconds # then red for 20 seconds. def cycle_lights(): # Go yellow. stoplight[2].off() stoplight[1].on() # Wait 2 seconds utime.sleep(2) # Go red and turn on walk light stoplight[1].off() stoplight[0].on() utime.sleep_ms(500) # Give the pedestrian a chance to see it pedestrian_signal[0].off() pedestrian_signal[1].on() # After 10 seconds, start blinking the walk light utime.sleep(1) for i in range(0,10): pedestrian_signal[1].off() utime.sleep_ms(500) pedestrian_signal[1].on() utime.sleep_ms(500) # Stop=green, walk=red pedestrian_signal[1].off() pedestrian_signal[0].on() utime.sleep_ms(500) # Give the pedestrian a chance to see it stoplight[0].off() stoplight[2].on() Мы назовем функцию кнопки button_pressed(). Эта функция используется как функция обратного вызова для прерывания нажатия кнопки. Технически
Написание кода  315 MicroPython связывает этот метод с прерыванием на выводе кнопки (см. далее). В этой функции есть один элемент, требующий пояснения. Когда мы используем такой компонент, как кнопка, и пользователь ее нажимает, контакты в кнопке не переходят из выключенного состояния во включенное состояние мгновенно. Существует очень небольшой период, когда считываемое значение нестабильно. Таким образом, мы не можем просто сказать «когда вывод становится высоким», потому что значение, считанное на выводе, может быстро «прыгать» от низкого уровня к высокому (или от высокого к низкому). Это явление называется дребезгом. Мы можем преодолеть его искусственно с помощью разных методов, называемых устранением дребезга. Например, мы можем проверять значение вывода, к которому подключена кнопка, несколько раз с течением времени и фиксировать нажатие кнопки только тогда, когда значение остается стабильным в течение этого времени. Код устранения дребезга показан в лис­тин­ге 9.3. Обратите внимание, что в цикле мы ожидаем значения 50 миллисекунд. Если показания совпадают, мы вызываем функцию cycle_lights(). Листинг 9.3  Функция button_pressed() # Create callback for the button def button_pressed(line): cur_value = button.value() active = 0 while (active < 50): if button.value() != cur_value: active += 1 else: active = 0 utime.sleep_ms(1) print("") if active: cycle_lights() else: print("False press") СОВЕТ Дополнительную информацию о явлении дребезга и методах его предотвращения можно найти по адресу www.eng.utah.edu/~cs5780/debouncing.pdf. Наконец, нам нужно настроить кнопку на вызов функции button_pressed(), когда плата обнаруживает прерывание. Следующий код устанавливает функцию обратного вызова с использованием прерывания на выводе, к которому подключена кнопка: # Create an interrupt for the button button.irq(trigger=Pin.IRQ_RISING, handler=button_pressed) Теперь у нас все готово для тестирования кода. Откройте новый файл с именем pedestrian_crossing.py и введите приведенный код. В лис­тин­ге 9.4 показан полный код проекта.
316  Проект: пешеходный переход Листинг 9.4  Код моделирования пешеходного перехода1 # # MicroPython for the IoT Second Edition - Chapter 09 # # Chapter 09 – Pedestrian Crosswalk # # This example implements a Pedestrian Crosswalk Simulator # controlling LEDs and button as input # # Dr. Charles Bell # # Import libraries from machine import Pin import time # Setup the button and LEDs # Pins for the Arduino Nano RP2040 Connect #stoplight_red = Pin(13, Pin.OUT) #stoplight_yellow = Pin(12, Pin.OUT) #stoplight_green = Pin(29, Pin.OUT) #button = Pin(25, Pin.IN, Pin.PULL_DOWN) #pedestrian_red = Pin(27, Pin.OUT) #pedestrian_green = Pin(26, Pin.OUT) # Pins for the Pico stoplight_red = Pin(13, Pin.OUT) stoplight_yellow = Pin(12, Pin.OUT) stoplight_green = Pin(11, Pin.OUT) button = Pin(9, Pin.IN, Pin.PULL_DOWN) pedestrian_red = Pin(8, Pin.OUT) pedestrian_green = Pin(7, Pin.OUT) # Setup lists for the LEDs stoplight = [stoplight_red, stoplight_yellow, stoplight_green] pedestrian_signal = [pedestrian_red, pedestrian_green] # Turn off the LEDs for led in stoplight: led.off() for led in pedestrian_signal: led.off() # Start with green stoplight and red pedestrian_signal stoplight[2].on() pedestrian_signal[0].on() # We need a method to cycle the stoplight and pedestrian_signal 1 В данном случае код рассчитан на выполнение на Pico, настройки для Nano RP2040 закомментированы. – Прим. перев.
Написание кода  317 # # We toggle from green to yellow for 2 seconds # then red for 20 seconds. def cycle_lights(): print("HERE") # Go yellow. stoplight[2].off() stoplight[1].on() # Wait 2 seconds time.sleep(2) # Go red and turn on walk light stoplight[1].off() stoplight[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it pedestrian_signal[0].off() pedestrian_signal[1].on() # After 10 seconds, start blinking the walk light time.sleep(1) for i in range(0,10): pedestrian_signal[1].off() time.sleep_ms(500) pedestrian_signal[1].on() time.sleep_ms(500) # Stop=green, walk=red pedestrian_signal[1].off() pedestrian_signal[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it stoplight[0].off() stoplight[2].on() # Create callback for the button def button_pressed(line): cur_value = button.value() active = 0 while (active < 50): if button.value() != cur_value: active += 1 else: active = 0 time.sleep_ms(1) print("") if active: cycle_lights() else: print("False press") # Create an interrupt for the button button.irq(trigger=Pin.IRQ_RISING, handler=button_pressed) Хорошо, теперь мы готовы реализовать проект.
318  Проект: пешеходный переход Выполнение кода Наконец, мы подошли к моменту, когда можем скопировать все файлы на плату и выполнить код проекта. Еще раз обязательно проверьте все аппаратные соединения перед подключением Pico к компьютеру. Затем скопируйте файлы кода на Pico и выполните скрипт. Вы можете создать каталог на своем Pico для размещения кода, если хотите, чтобы все было в порядке, как показано на рис. 9.3. Рис. 9.3  Файлы модели пешеходного перехода на плате MicroPython Загрузив файл pedestrian_crossing.py на Pico, просто нажмите кнопку Run (Выполнить) и наблюдайте за выполнением кода! Если все подключено правильно и код верен, вы увидите горящий зеленый светодиод стоп-сигнала и красный светодиод пешеходного перехода. Затем вы можете нажать кнопку перехода и наблюдать, как светофор меняет цвет с зеленого на желтый, а затем на красный. После этого цвет пешеходного перехода изменится с красного на зеленый и затем начнет мигать. Когда время таймера истечет, цвет пешеходного перехода изменится с зеленого на красный, а цвет светофора – с красного на зеленый. Если что-то не работает, вернитесь и проверьте код1. 1 В коде по листингу 9.4 есть неясный момент, связанный с прерыванием. Раз объявленное, прерывание будет выполняться каждый раз по наступлении заявленного события, в данном случае по возрастающему (RISING) фронту импульса на выводе, к которому подключена кнопка. А этот фронт будет возникать при каждом дребезге, т. е. неоднократно при нажатии – независимо от проверки состояния кнопки внутри функции. Легальным методом борьбы со множественным возникновением прерывания является отключение прерывания сразу в первом вызове его обработчика и включение заново тогда, когда опасность дребезга миновала (в данном случае после выполнения всего кода функции button_pressed(), т. к. в ней проводится проверка на дребезг). Рекомендуется учесть это замечание при запуске кода на реальной плате и при необходимости исправить его. – Прим. перев.
Итоги  319 А что дальше? Этот проект дает отличные перспективы для повторного использования методик в других проектах. Теперь мы научились использовать один из самых ходовых компонентов – светодиоды. И следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые вы можете рассмотреть. Иные из них просты, а некоторые могут оказаться сложной задачей или потребовать дополнительных исследований и более сложного кодирования:   используйте NeoPixels (www.adafruit.com/category/168) вместо свето­ диодов. Это RGB-светодиоды, поэтому вам понадобится всего один-два для светофора и один для пешеходного сигнала. См. https://github.com/ JanBednarik/micropython-ws2812 для получения дополнительной информации и примеров;   используйте OLED из проекта главы 8 вместо светодиодов для указателя пешехода, чтобы показывать «Идти» или «Стоять»1;   добавьте еще один светофор для встречного движения, чтобы завершить симуляцию пешеходного перехода через улицу;   добавьте еще три светофора и расширьте симуляцию, включив в нее управление светофорами в двух направлениях. К этому моменту на вашей макетной плате будет много проводов, поэтому вам, возможно, придется использовать вторую макетную плату, чтобы сохранить всю проводку в порядке;   когда у вас заработают четыре светофора, добавьте второй пешеходный переход на другом направлении перекрестка. Конечно, если вы хотите сразу перейти к следующему проекту, вы можете это сделать, но потратьте некоторое время на изучение этих потенциальных доработок – это будет хорошей практикой. Итоги Работа с дискретными электронными компонентами может доставлять массу удовольствия. Когда вы только начинаете заниматься электроникой, просто заставить схему работать – это настоящий подвиг. Теперь, когда мы знаем намного больше об управлении Pico и оборудованием, подключенным к GPIO, мы можем увидеть, насколько легкий для освоения язык имеется в нашем распоряжении, такой как MicroPython. 1 Заметим, что дисплей 128×32 с контроллером ssd1306 является графическим, т. е. допускает рисование произвольных фигур, так что вполне можно изобразить идущего/стоящего человечка, как на настоящем пешеходном переходе. – Прим. перев.
320  Проект: пешеходный переход В этой главе мы реализовали симуляцию кнопки пешеходного перехода и светофора. Мы использовали серию светодиодов для обозначения светофора и сигнала пешехода. Мы также добавили аппаратную кнопку для имитации нажатия на реальном переходе. Если вам понравился этот проект, следующие два проекта вам понравятся еще больше. В следующей главе мы рассмотрим проект, который поможет вам дома вовремя поливать растения. Мы также узнаем, как просмотреть данные с датчиков на веб-странице.
Глава 10 Проект: мониторинг растений Одними из наиболее распространенных форм проектов в области электроники являются те, которые отслеживают события с помощью датчиков, передающих данные либо на другой компьютер (например, в облачную службу), либо на локальный сервер. Один из способов сделать это – подключить плату MicroPython к набору датчиков и затем записать данные. В интернете можно найти несколько примеров общих регистраторов данных, но лишь немногие сочетают регистрацию данных с компонентом визуализации. Действительно, осмысление данных является ключом к созданию успешного проекта. В этой главе мы не будем сразу переходить к запуску нашего проекта в интернете. Скорее, мы начнем с основ и рассмотрим объединение регистрации данных с визуализацией данных с помощью веб-сервера, который запустим на той же плате MicroPython. Мы также увидим, как использовать аналоговый датчик, который выдает аналоговые данные, которые затем нам придется интерпретировать. Мы будем использовать возможности аналого-цифрового преобразования (АЦП) Pico, чтобы получить в конечном итоге значение, которое мы можем использовать. Наконец, мы снова будем использовать модуль RTC, описанный в главе 8. Как вы увидите, код, применяемый в этой главе, более модульный и использует больше функций, чем предыдущие проекты. Этот код несложно изучить, и он использует концепции, которые мы видели в предыдущих главах. Наиболее сложными являются веб-сервер и аналоговое оборудование. Обзор В этой главе мы реализуем решение для мониторинга влагосодержания почвы у растений (для краткости – мониторинг растений). Это потребует
322  Проект: мониторинг растений использования одного или нескольких датчиков влагосодержания почвы, подключенных к плате MicroPython. Мы настроим сигнал таймера (через прерывание), который будет периодически запускать считывание данных с датчиков и сохранение их в табличном файле (типа CSV1). На рис. 10.1 изображена концептуальная схема проекта. Плата MicroPython будет считывать данные с датчиков влагосодержания, а затем по запросу отображать их через веб-страницу в HTML-формате. Рис. 10.1  Концепция проекта мониторинга растений Пользовательский интерфейс этого проекта представляет собой веб-стра­ ни­цу с таблицей, содержащей все данные, считанные из log-файла. Именно так мы можем преодолеть потенциальные проблемы циклической работы с HTML-сервером. То есть нам не нужно прерывать цикл отправки данных для чтения датчиков – это делается через таймер чтения. Отделив снятие показаний датчика от их визуализации, мы можем повторно использовать или изменять их, не путаясь при изучении кода. Например, пока компонент визуализации считывает данные датчика из файла, для кода чтения датчика не имеет значения, как они используются. Единственным интерфейсом, или связью между этими двумя частями, является формат log-файла, а поскольку мы используем файл CSV, его очень легко читать и использовать в нашей программе. Чтобы сделать работу интереснее и упростить кодирование, мы поместим весь код датчика в отдельный модуль кода. Напомним, что этот метод может 1 Формат CSV (Comma-separated values или character-separated values) – это формат файлов, который хранит табличные данные (цифры и текст) в формате простого текста с разделением каким-либо символом (обычно запятыми). – Прим. перев.
Необходимые компоненты  323 использоваться для уменьшения объема кода в любом модуле, тем самым упрощая его написание и поддержку. Теперь посмотрим, какие компоненты нужны для этого проекта, а затем соединим их воедино. Необходимые компоненты В табл. 10.1 перечислены компоненты, которые вам понадобятся в дополнение к плате MicroPython и кабелю USB. Таблица 10.1. Необходимые компоненты Компонент Кол-во Описание Ссылка Датчик влагосодержания почвы 1+ Резистивный www.sparkfun.com/products/13637 датчик влажности для почвы RTC 1 Часы реального времени www.sparkfun.com/products/13637 www.adafruit.com/product/3296 Перемычки 3 (мин) Перемычки «штырь/штырь» www.sparkfun.com/products/8431 Датчики влажности почвы бывают разных форматов, но большинство из них имеют два штыря, которые вставляются в почву и с помощью небольшого электрического тока измеряют сопротивление между зубцами. Чем выше показанное условное значение, тем больше влаги в почве. Однако для получения надежных и реалистичных пороговых значений необходима некоторая калибровка. Хотя у производителя есть рекомендации по пороговым значениям, для подбора правильных значений могут потребоваться некоторые эксперименты. На эти датчики также могут влиять факторы окружающей среды, включая объем горшка, в котором находится растение, состав почвы и другие факторы. Таким образом, экспериментирование с переувлажненной почвой, сухой почвой и почвой, за которой правильно ухаживают, поможет вам установить пороговые значения для ваших конкретных условий. На рис. 10.2 показан датчик влагосодержания почвы от SparkFun, у которого вместо штыревого разъема имеется винтовая клемма. Вы можете найти несколько разновидностей этих датчиков. Просто выберите тот, который вы хотите использовать, учитывая, что в случае обычных разъемов вам могут понадобиться разные перемычки для подключения его к плате1. 1 В отечественной рознице предлагается много разновидностей датчиков влагосодержания в почве (емкостные, резистивные, индукционные). С точки зрения интерфейса многие из них одинаковы, различия имеются лишь в способе подключения проводников и величине аналогового сигнала на выходе. Однако любой такой датчик нужно калибровать по месту, потому различия в сигнале не имеют значения. – Прим. перев.
324  Проект: мониторинг растений Рис. 10.2  Датчик влагосодержания почвы (с разрешения sparkfun.com) Особо следует отметить обращение с этими датчиками влагосодержания. Если вы оставите такие датчики включенными, со временем они могут портиться. Металл на зубцах может корродировать из-за электролиза, что резко сокращает срок его службы. Вы можете использовать вывод GPIO для питания датчика, включая напряжение только тогда, когда хотите прочитать значение. Имейте в виду, что будет небольшая задержка, пока датчик установится, и необходимо будет подождать, а затем прочитать значение и выключить датчик. Таким образом, мы можем значительно продлить срок службы датчика1. К счастью, схема этого проекта менее сложна, чем у двух предыдущих. Давайте посмотрим, как соединить компоненты вместе. Настройка оборудования Аппаратные установки для этого проекта будут отображать только два датчика, но при желании вы можете добавить еще несколько. Рекомендуется начать с одного датчика, пока проект не заработает, а затем добавлять дополнительные. Для простоты вы можете использовать макетную плату при подключении датчиков к плате MicroPython, но в зависимости от того, как и где вы планируете разместить плату контроллера, макетная плата может вам не понадобиться. Конечно, вы должны вставить датчики влагосодержания в почву около ваших растений. Если растения расположены далеко от источника питания, возможно, вам придется использовать более длинные провода для подключения датчиков. Вам следует начать с одной установки и одного датчика (или двух датчиков на одной установке), которые вы можете разместить рядом с вашим ПК или источником питания. 1 Еще более эффективный способ – питание подобного датчика переменным током, но и он не избавляет от износа электродов и, следовательно, постепенной утери датчиком чувствительности. – Прим. перев.
Настройка оборудования  325 Рассмотрим, как подключить Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как вы увидите, для внешних плат соединения аналогичны, но места подключения к платам контроллеров различаются. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 10.2 показаны подключения, необходимые в этом проекте для Raspberry Pi Pico. Если вы приобрели датчики влагосодержания, аналогичные показанным выше от SparkFun, в них используются винтовые клеммы, а не гнездовые разъемы. Подойдут те же самые перемычки, но вам понадобится небольшая отвертка, чтобы прикрепить штыри перемычек к винтовым клеммам. Таблица 10.2. Соединения для проекта мониторинга растений (Pico) Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на внешней плате 27 GP20 Датчик 1 – VCC 32 GP27 Датчик 1 – SIG – Макетная плата (общий) Датчик 1 – GND 29 GP21 Датчик 2 – VCC 34 GP28 Датчик 2 – SIG – Макетная плата (общий) Датчик 2 – GND 28 GND Макетная плата (общий) 40 5V RTC – VCC – Макетная плата (общий) RTC – GND 11 GP8 RTC – SDA 12 GP9 RTC – SCL На рис. 10.3 показана схема подключения для проекта Pico. ВНИМАНИЕ! В связи с тем, что некоторые платы MicroPython ограничивают выходное напряжение на выводах до 3,3 В, вам потребуются датчики влажности почвы, работающие при напряжении 3,3–5 В. Указанные датчики от SparkFun совместимы. Соединения для Arduino Nano RP2040 Connect Поскольку мы используем те же компоненты для Arduino Nano RP2040 Connect, соединения будут аналогичными, но, как отмечалось в предыдущих главах, Nano RP2040 имеет другое расположение контактов. Таким образом, некоторые соединения будут отличаться. В табл. 10.3 показаны соединения, необходимые для Nano RP2040.
326  Проект: мониторинг растений Рис. 10.3  Подключение проекта мониторинга растений (Pico) Таблица 10.3. Соединения для проекта мониторинга растений (Nano RP2040) Обозначение на плате Nano RP2040 (номер GPIO / функция) Соединение D8 (GP20) Датчик 1 – VCC A1 (GP27) Датчик 1 – SIG Макетная плата (общий) Датчик 1 – GND D9 (GP21) Датчик 2 – VCC A2 (GP28) Датчик 2 – SIG Макетная плата (общий) Датчик 2 – GND GND Макетная плата (общий) 5V RTC – VCC GND RTC – GND A4 (GP12. SDA) RTC – SDA A5 (GP13, SCL) RTC – SCL На рис. 10.4 показана схема соединений для проекта Nano RP2040. Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте плату – необходимо немало обсуждений, прежде чем мы будем готовы протестировать проект.
Написание кода  327 Рис. 10.4  Соединения проекта мониторинга растений (Nano RP2040) Написание кода Теперь пришло время написать код для нашего проекта. К счастью, код может работать как на Pico, так и на Nano RP2040 Connect с небольшими изменения­ ми, которые будут выявлены при обсуждении основной части кода. Код длиннее, чем тот, который мы видели до сих пор, и из-за этого лучше всего разделить проект на части. Мы собираемся написать код в два этапа. До конца у нас не будет рабочего проекта, поэтому большая часть обсуждения будет посвящена отдельным частям. Мы соберем все это вместе перед тестированием проекта. У нас будет два основных компонента: основной код и кодовый модуль, включающий работу с датчиками влагосодержания почвы. Код HTML-сервера и вспомогательные функции мы разместим в основном модуле кода. Однако прежде чем мы приступим к написанию кода, нам следует откалибровать датчики. Делается это следующим образом. Калибровка датчика Калибровка датчиков особенно актуальна для датчиков влагосодержания поч­вы, поскольку, во-первых, существует множество различных версий таких датчиков; во-вторых, эти датчики очень чувствительны к составу почвы,
328  Проект: мониторинг растений температуре и даже типу горшка, в котором живет растение. Таким образом, нам следует поэкспериментировать с почвой известной влажности, чтобы знать, какие диапазоны использовать в нашем коде. Точнее, мы хотим классифицировать показания датчика, чтобы можно было определить, нуждается ли растение в поливе. Мы будем использовать значения «dry» (сухая), «ok» и «wet» (влажная) для классификации значения, считываемого с датчика. Увидев эти характеристики, нам гораздо легче с первого взгляда определить, нуждается ли растение в поливе. Необработанные данные, такие, к примеру, как значение 1756, ничего не говорят, но если мы видим «сухой», то знаем, что растению нужна вода. Поскольку датчики являются аналоговыми датчиками, мы будем использовать аналого-цифровое преобразование на плате. Когда мы прочитаем уровень напряжения на выводе, то получим значение в диапазоне 0–4096. Это значение связано с сопротивлением, которое датчик измеряет в почве. Низкие значения указывают на сухую почву, а высокие значения – на влажную почву. Однако датчики разных производителей могут сильно различаться по считываемым значениям. Например, датчики от SparkFun обычно считывают значения в диапазоне 0–1024, а датчики других производителей могут считывать значения до 4096. К счастью, все они, похоже, едины в том, что чем ниже значение, тем суше почва1. Итак, мы должны определить пороговые значения для трех степеней классификации. Мы знаем, что существует несколько факторов, которые могут повлиять на значения, считываемые с датчика. Таким образом, вам следует выбрать несколько горшков с почвой (и желательно с наличием растений2), включая один, который, по вашему мнению, сухой, другой, который правильно полит, и третий, который переувлажнен. Лучше всего выбрать тот, который сухой, провести измерения, затем поливать его до тех пор, пока влажность почвы не станет нужной, измерить его, а потом снова поливать, пока воды не станет слишком много. Чтобы определить порог, мы должны сначала написать небольшой код, чтобы настроить плату для считывания значений с датчика. Это включает в себя выбор контакта GPIO, поддерживающего АЦП. Проверьте разводку выводов вашей платы, чтобы убедиться, какие контакты GPIO обладают функциями аналогового входа. Также нужно выбрать контакт, который будет использоваться для питания платы. Наконец, мы напишем цикл для чтения нескольких значений каждые пять секунд и последующего их усреднения. Пять секунд – это произвольное значение, оно было получено на основе технического описания датчика. Проверьте свои датчики, чтобы узнать, сколько времени необходимо для 1 2 Иными словами, датчик показывает величину, пропорциональную не сопротивлению почвы, а обратной величине – проводимости. – Прим. перев. Обязательно выберите растение, достаточно крепкое, чтобы выдерживать ваши эксперименты. – Прим. авт.
Написание кода  329 стабилизации показаний (возможно, под заголовком «частота считываний»). В лис­тин­ге 10.1 показан код, необходимый для настройки аналого-цифрового канала, вывод для питания датчика и цикл для считывания десяти значений и их усреднения. Листинг 10.1  Калибровка порогов влажности почвы # # MicroPython for the IoT Second Edition - Chapter 10 # # Chapter 10 - Plant Monitoring # # This file contains code to capture the range of values # for a soil moisture sensor. # # Dr. Charles Bell # # Import libraries from machine import ADC, Pin import time print("MicroPython for the IoT - Soil Moisture threshold test.") # Setup the GPIO pin for powering the sensor. power = Pin(20, Pin.OUT) # Setup the ADC for the signal pin adc = ADC(Pin(27)) # Turn sensor off power.low() # Loop 10 times and average the values read print("Reading 10 values.") total = 0 for i in range (0,10): # Turn power on power.high() # Wait for sensor to power on and settle time.sleep(5) # Read the value value = adc.read_u16() print("Value read ({0:02}): {1}".format(i+1, value)) total += value # Turn sensor off power.low() # Now average the values print("The average value read is: {0}".format(total/10)) Если вы введете этот код в файл с именем threshold.py, то сможете скопировать его на свою плату MicroPython и выполнить. В лис­тин­ге 10.2 показаны результаты выполнения этой калибровочной программы у меня на растении, которое правильно поливается.
330  Проект: мониторинг растений Листинг 10.2  Запуск калибровочной программы >>> import threshold Reading 10 values. Value read: 1724 Value read: 1983 Value read: 1587 Value read: 1702 Value read: 1634 Value read: 1525 Value read: 1874 Value read: 1707 Value read: 1793 Value read: 1745 The average value read is: 1727.4 >>> Здесь мы видим среднее значение 1727 (результат в подобных программах округляется до целых чисел). Дальнейшие испытания на сухой почве дали значение 425, а на переувлажненном растении – 3100. Таким образом, пороговые значения для этого примера примем равными 500 для сухой и 2500 для влажной почвы. Однако в вашем случае результаты могут сильно отличаться, поэтому обязательно запустите эту программу с вашими датчиками, платой и выбранными условиями для растений. СОВЕТ Чтобы упростить калибровку пороговых значений, используйте датчики одного и того же производителя. В противном случае вам, возможно, придется использовать отдельный набор пороговых значений для каждого поддерживаемого датчика1. Обратите внимание на прочитанные значения. Как видите, значения могут меняться от момента к моменту. Это нормально для этих датчиков. Они известны тем, что производят колеблющиеся значения. Именно поэтому мы производим отбор проб с датчика более одного раза, чтобы получить среднее значение за некоторый период, а не одно-единственное значение. Среднее значение может быть искажено, если одна или несколько проб сильно отклоняются. Однако выборка хотя бы десяти значений и усреднение помогут снизить вероятность получения аномальных показаний. Мы сделаем это в коде нашего проекта. Теперь, когда у нас есть пороговые значения для датчиков, мы можем начать с модуля кода для датчиков. Часть 1. Модуль кода датчика Первая часть проекта будет заключаться в создании модуля кода, содержащего новый класс SoilMoisture, который содержит функции для чтения данных 1 Все равно следует калибровать индивидуально, как минимум для случая неновых резистивных датчиков. Иначе есть большая вероятность пребывания растения в болоте амазонских джунглей, когда датчик показывает пустыню Сахару. – Прим. перев.
Написание кода  331 с датчиков и сохранения данных в файл. В этом разделе мы покажем, как написать код модуля. Откройте новый файл и назовите его soil_moisture.py. Начнем с рассмотрения высокоуровневого дизайна. Высокоуровневый дизайн Как мы узнали ранее, разумной идеей является создание дизайна для каждого модуля кода (класса), который мы хотим использовать. Мы будем использовать модуль кода из основного кода. Таким образом, нам нужны функции, которые сообщат классу, что нужно читать датчики, и способ получить имя файла, который класс использует для данных. Обычно создается модуль кода, который полностью скрывает файл и все операции с ним, но в этом случае класс занимается только чтением датчиков и записью данных. Мы будем использовать прерывание таймера для чтения датчиков. Это позволяет нам настроить периодический вызов функции без необходимости отслеживать или опрашивать время и вызывать функцию напрямую. Для удобства мы предоставляем полную функцию ведения log-файла. Таким образом, нам понадобятся только две публичные функции: одна для очистки log-файла, а другая – для получения его имени. Помимо инициализации класса, нам нужно получать имя файла только тогда, когда мы хотим обновить данные (отправить их клиенту). В табл. 10.4 показаны функции для класса SoilMoisture. Таблица 10.4. Общее построение (функции) класса SoilMoisture Функция __init__() Параметры Описание rtc, csv_filename, sensor_list Инициализация класса (конструктор) clear_log() Очистка log-файла _format_time() Форматирование времени для отображения и сохранение в log-файл _get_value() adc, power Считывание показаний датчика десять раз и усреднение считанных значений read_sensors() Считать значения с датчика и сохранить их в log-файле _convert_value() value Преобразование необработанного значения датчика в значение порога Обратите внимание на первую функцию с именем __init__(). Это конст­ руктор класса, который будет вызываться при создании экземпляра класса из нашего основного кода. Обратите также внимание, что имена частных методов обозначаются одним подчеркиванием. Далее объясняются код инициализации и необходимые функции. Полный код мы увидим в следующем разделе.
332  Проект: мониторинг растений Настройка и инициализация В этом разделе мы обсудим действия, которые нужны для настройки и инициализации модуля кода. Во-первых, нам нужно импортировать библиотеки, в том числе для аналого-цифрового преобразователя, выводов GPIO и функций операционной системы. Во-вторых, нам также понадобятся некоторые константы, определенные для класса. Напомним, мы хотим классифицировать влагосодержание поч­ вы, считанное с помощью числовых значений. Для этого нам нужно будет использовать пороговые значения, которые мы определили ранее для классификации. Мы можем использовать заданные константы в начале файла, чтобы их было легче изменить позже, если нам понадобится настроить код для использования с другими датчиками или изменить условия для растений (другой горшок, почва, окружающая среда и т. д.). Мы можем использовать этот же подход, чтобы задать имя файла, содержащего данные. Мы также зададим константу для определения частоты считывания данных датчиков. Поскольку мы будем использовать цикл для чтения датчика, ожидая каждого чтения по 5 секунд, нам понадобится минимум 50–55 секунд, чтобы прочитать десять значений. Таким образом, мы не можем установить частоту обновления (указанную в секундах) менее одной минуты. Хотя вы можете установить более низкое значение для тестирования, в реальности вы, конечно, не захотите проверять влажность почвы ваших растений каждую минуту. Как часто вы обычно проверяете свои растения? Раз в несколько дней или раз в день? Зачем проверять это чаще обычного? Частота дискретизации Частота выборки данных с датчика нередко упускается из виду при проектировании сетей датчиков. Тенденция заключается в том, чтобы хранить как можно больше значений, думая, что чем больше данных, тем лучше. Но этот подход неприменим в общем случае. В нашем проекте мониторинга растений: если вы обычно проверяете свои растения один раз в день, какую пользу вам принесет отбор проб с датчиков каждые пять минут? Это приведет только к избытку данных! Частота выборки должна быть тщательно продумана, дабы предоставить данные, необходимые для того, чтобы сделать выводы, не создавая при этом слишком много данных. Хотя больше данных, конечно, лучше, чем их недостаток, чтение данных слишком часто может генерировать такой объем данных, что он превысит емкость вашего устройства. При разработке проектов, в которых осуществляется отбор проб с датчиков, следует тщательно рассчитывать частоту выборки. Как правило, если вы осуществляете выборку данных, которые могут изменяться очень медленно, частота выборки должна быть низкой. Данные выборки, которые могут меняться быстрее, должны иметь меньший промежуток между выборками. Наконец, нам нужна функция, которая преобразует структуру времени в строку. Напомним, мы можем использовать простую спецификацию фор-
Написание кода  333 мата из примера применения RTC в главе 8. Для этого действия мы будем использовать частную функцию на уровне модуля. В лис­тин­ге 10.3 показан код раздела настройки и инициализации. Помес­ тите его в начало файла. Листинг 10.3  Настройка и инициализация класса SoilMoisture import os import time from machine import ADC, Pin # Thresholds for the sensors LOWER_THRESHOLD = 500 UPPER_THRESHOLD = 2500 UPDATE_FREQ = 120 # seconds # Format the time (epoch) for a better view def _format_time(tm_data): # Use a special shortcut to unpack tuple: *tm_data return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format( *tm_data) Конструктор Конструктор класса – это модуль, в котором происходит вся основная работа. Нам нужно сделать несколько вещей, в том числе следующие:   инициализировать все переменные класса;   настроить датчики в словаре, хранящемся в списке. Мы используем словарь для каждого датчика, таким образом мы можем определить контакт для данных датчика, контакт для питания датчика, номер датчика (произвольный идентификатор) и расположение датчика. Затем мы помещаем эти словари в список, чтобы упростить чтение со всех датчиков одновременно с помощью цикла. В лис­тин­ге 10.4 показан код конструктора класса. Листинг 10.4  Конструктор класса SoilMoisture # Initialization for the class (the constructor) def __init__(self, rtc, csv_filename, sensor_list): self.rtc = rtc # Try to access the file system and make the new path self.sensor_file = csv_filename # Loop through the sensors specified and setup a new dictionary # for each sensor that includes the power and ADC pins defined. self.sensors = [] for sensor in sensor_list: # Setup the dictionary for each soil moisture sensor soil_moisture = { 'sensor': ADC(Pin(sensor['pin'])), 'power': Pin(sensor['power'], Pin.OUT),
334  Проект: мониторинг растений 'location': sensor['location'], 'nick': sensor['nick'] } self.sensors.append(soil_moisture) print("Soil moisture sensors are ready...") Общедоступные (public) функции Общедоступных функций всего две. Первая, clear_log(), просто открывает файл для записи и закрывает его. Это эффективно очищает файл. Функция предусмотрена для удобства. Вторая функция, read_sensors(), считывает датчики и записывает данные в log-файл. Приватные (private) функции Есть три приватные функции. Функция _get_value() – это тот же код, что и программа калибровки порога, где мы проверяем датчик десять раз и усредняем значение. Функция _convert_value() – вспомогательная функция для определения состояния почвы на основе данных датчика. Эта функция возвращает строку «dry», «ok» или «wet». Функция _format_time() получает текущее время и форматирует его для записи в журнал. Полный код Теперь, когда мы рассмотрели все части модуля кода, давайте посмотрим на готовый код. В лис­тин­ге 10.5 показан полный код модуля SoilMoisture проекта мониторинга растений Plant Monitor. Напоминаем, что сохраняем этот файл как soil_moisture.py. Листинг 10.5  Полный код модуля SoilMoisture # # MicroPython for the IoT Second Edition - Chapter 10 # # MicroPython Plant Monitoring # # Soil Moisture class # import os import time from machine import ADC, Pin # Thresholds for the sensors LOWER_THRESHOLD = 500 UPPER_THRESHOLD = 2500 UPDATE_FREQ = 120 # seconds # Format the time (epoch) for a better view def _format_time(tm_data): # Use a special shortcut to unpack tuple: *tm_data return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format( *tm_data)
Написание кода  335 class SoilMoisture: """ This class reads soil moisture from one or more sensors and writes the data to a comma-separated value (csv) file as specified in the constructor. It also requires a list (array) or sensor dictionaries in the following form. sensor = { 'pin': sensor_pin, 'power': power_pin, 'location': location or description 'nick': nickname for the sensor } Sensors are numbered in the order they appear in the list. Note: Intermediate calls to get_values() will return the last value(s) read or None if no sensors have been read. """ # Initialization for the class (the constructor) def __init__(self, rtc, csv_filename, sensor_list): self.rtc = rtc # Try to access the file system and make the new path self.sensor_file = csv_filename # Loop through the sensors specified and setup a new dictionary # for each sensor that includes the power and ADC pins defined. self.sensors = [] for sensor in sensor_list: # Setup the dictionary for each soil moisture sensor soil_moisture = { 'sensor': ADC(Pin(sensor['pin'])), 'power': Pin(sensor['power'], Pin.OUT), 'location': sensor['location'], 'nick': sensor['nick'] } self.sensors.append(soil_moisture) print("Soil moisture sensors are ready...") # Clear the log def clear_log(self): log_file = open(self.sensor_file, 'w') log_file.close() # Format the time (epoch) for a better view def _format_time(self): # Get datetime dt = self.rtc.datetime # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) return "{0:02}/{1:02}/{2:04} {3:02}:{4:02}:{5:02}".format(
336  Проект: мониторинг растений dt[1], dt[2], dt[0], dt[3], dt[4], dt[5]) # Read the sensor 10 times and average the values read def _get_value(self, adc, power): total = 0 # Turn power on power.high() # Wait for sensor to power on and settle time.sleep(5) for i in range (0,10): time.sleep(1) # Read the value value = adc.read_u16() total += value # Turn sensor off power.low() return int(total/10) # Monitor the sensors, read the values, and save them def read_sensors(self): log_file = open(self.sensor_file, 'a') self.values_read = [] for sensor in self.sensors: # Read the data from the sensor and convert the value value = self._get_value(sensor['sensor'], sensor['power']) print("Value read: {0}".format(value)) # datetime,num,value,enum,location message = ("{0},{1},{2},{3},{4}" "".format(self._format_time(), sensor['nick'], value, self._convert_value(value), sensor['location'])) log_file.write("{0}\n".format(message)) log_file.close() # Convert the raw sensor value to an enumeration def _convert_value(self, value): # If value is less than lower threshold, soil is dry else if it # is greater than upper threshold, it is wet, else all is well. if (value <= LOWER_THRESHOLD): return "dry" elif (value >= UPPER_THRESHOLD): return "wet" return "ok" Уделите некоторое время изучению кода, пока не поймете все разделы. В этом классе отсутствует одна вещь: событие чтения для получения показаний датчиков. Напомним, мы не хотим, чтобы HTML-код (веб-сервер) тратил время на считывание датчиков, поскольку он будет ожидать соединений от клиентов. Таким образом, нам нужен способ периодического независимого считывания данных с датчиков. Давайте посмотрим на код таймера чтения.
Написание кода  337 Часть 2. Код таймера чтения Код таймера чтения состоит из класса ReadTimer. В этой главе мы будем использовать событие таймера и обратный вызов, чтобы упростить задачу (в следующей главе мы увидим более продвинутый метод). Конструктор класса устанавливает экземпляр Timer и использует метод класса (называемый обратным вызовом) для выполнения задачи, когда происходит событие Timer. Мы будем использовать константу, чтобы установить частоту события чтения (DATA_READ_INTERVAL) в миллисекундах. Вместо того чтобы связать этот класс с классом SoilMoisture, мы предоставим две общедоступные функции: одну, чтобы узнать, пора ли читать, time_to_read(), и другую, чтобы сбросить таймер, reset(). Итак, как нам использовать этот класс? После создания экземпляра мы можем вызвать time_to_read(), который возвращает true, если таймер сработал, и, таким образом, мы можем использовать его, чтобы определить, можем ли мы прочитать датчики. Опытные читатели заметят, что этот метод не особенно точен и что событие чтения может произойти через некоторое время после его возникновения (именно поэтому в следующей главе мы увидим более совершенную технику). В лис­тин­ге 10.6 показан полный код класса ReadTimer. Вы можете сохранить этот файл как read_event.py. Листинг 10.6  Полный код модуля таймера чтения # # MicroPython for the IoT Second Edition - Chapter 10 # # Chapter 10 - Plant Monitoring # # This file contains a class to display data from one or more soil # moisture sensors. # # Dr. Charles Bell # # Import libraries from machine import Timer # Constants DATA_READ_INTERVAL = 30000 # Increase this interval as needed # Class to control reading data with a timer class ReadTimer: def __init__(self, interval=DATA_READ_INTERVAL): # Create and start the timer interrupt to read data self.interval = interval self.data_read_event = True self.read_timer = Timer() self.read_timer.init(period=self.interval,mode=Timer.PERIODIC, callback=self.read_data_event)
338  Проект: мониторинг растений # Callback for reading the data on the interval. def read_data_event(self, timer_obj): self.data_read_event = True self.read_timer.deinit() # Check to see if it is time to read def time_to_read(self): return self.data_read_event # Reset the read event boolean (timer doesn't reset) def reset(self): self.read_timer.init(period=self.interval,mode=Timer.PERIODIC, callback=self.read_data_event) self.data_read_event = False Уделите некоторое время прочтению кода и убедитесь, что вы понимаете, как он работает. Теперь мы готовы взглянуть на основной код. Часть 3. Основной код Главный модуль будет использовать файл для хранения основного HTML-кода (поскольку он не меняется) и одну строку для заполнения HTML-таблицы данными из файла. Мы также добавим функцию для чтения даты и времени из RTC. Мы можем вручную отформатировать команду вместе с URL-адресом. Можно использовать этот метод, чтобы команды выполнялись без использования кнопок или других функций пользовательского интерфейса. Это также помогает усложнить использование этих команд, чтобы предотвратить чрезмерное применение. Например, мы можем предоставить команду очистки log-файла. Мы бы использовали URL-адрес типа http://192.168.42.140/CLEAR_ LOG, который отправляет запрос GET на HTML-сервер. Мы можем перехватить эту команду и очистить log-файл при ее выполнении. ВНИМАНИЕ! Если вы создаете подобные команды, используйте их осторожно. То есть, установив URL-адрес http://192.168.42.140/CLEAR_LOG и нажав <Enter>, вы тем самым вводите команду. Обновление страницы приведет к повторному вводу команды! При применении этого способа обязательно очистите URL-адрес перед обновлением или, что лучше, используйте его один раз и сразу закройте страницу/вкладку. В следующих разделах объясняются код инициализации и необходимые функции. Полный код мы увидим в конце. Начнем с HTML-кода. HTML-код (отдельные файлы) Мы будем хранить необходимый HTML-код в отдельных файлах для экономии памяти. Напомним, что при чтении файла построчно нам не нужно занимать место строками в коде программы. По мере усложнения проектов это
Написание кода  339 могло бы стать проблемой. Таким образом, данный проект демонстрирует способ сэкономить немного памяти. HTML для этого проекта создает веб-страницу с простой таблицей, которая включает все данные в файле на момент запроса. Чтобы упростить задачу, мы будем использовать три файла. Первый файл (с именем part1.html) будет содержать HTML-код вплоть до строк таблицы; второй файл (с именем plant_data.csv) будет заполнен данными в формате CSV; а третий (с именем part2.html) будет содержать оставшийся HTML-код. Первый файл part1.html показан в лис­тин­ге 10.7. Этот файл устанавливает HTML-код таблицы. Он также устанавливает характеристики таблицы, включая выравнивание текста, размер границы и отступы, – и все это посредством каскадного стиля CSS (тег <style>). Не волнуйтесь, если это выглядит странно или чуждо. Вы можете поискать в Google «стандарты W3C», чтобы узнать, как мы используем CSS для управления стилем веб-страницы. Листинг 10.7  HTML-код (part1.html) <!DOCTYPE html> <html> <head> <title>MicroPython for the IoT 2nd Edition - Plant Monitor</title> <meta http-equiv="refresh" content="30"> <style> table, th, td { border: 1px solid black; border-collapse: collapse; } th, td { padding: 5px; } th { text-align: left; } </style> </head> <center><h2>MicroPython for the IoT 2nd Edition - Plant Monitor</h2></center><br> <center>A simple project to demonstrate how to retrieve sensor data over the Internet. </center> <center><br><b>Plant Monitoring Data</b><br><br> <table style="width:75%"> <col width="180"> <col width="120"> <col width="100"> <col width="100"> <tr><th>Datetime</th><th>Name</th><th>Raw Value</th><th> Mois ture </th><th>Location</th></tr> Обратите внимание на код таблицы: это работает, и это очень просто. Те, кто знаком с HTML, возможно, захотят украсить и улучшить код. Последняя строка устанавливает заголовок таблицы.
340  Проект: мониторинг растений Второй файл, plant_data.csv, содержит данные с датчиков. Мы будем использовать константу для заполнения правильно отформатированной строки HTML-таблицы. Ниже показан пример того, как будет выглядеть строка данных в файле и как эти данные преобразуются в HTML. Мы покажем код HTML для строки таблицы в следующем разделе. # Raw data 9/23/2023 17:05,Ivy,500,ok,Green ceramic pot on top shelf # HTML table row <tr><td>2023-09-23 17:05</td><td>Ivy</td><td>500</td><td>ok</td><td>Green ceramic pot on top shelf </td></tr> Последний файл part2.html содержит закрывающие теги, поэтому он не очень большой. Но поскольку мы весь код читаем из файлов, то подключаем также этот файл. Файл содержит следующие строки: </table> </center> </html> Итак, как нам использовать эти файлы? Когда мы отправляем ответ обратно клиенту (веб-странице), мы читаем первый файл, отправляя по одной строке за раз, затем читаем файл данных, отправляя по одной строке за раз, потом читаем последний файл, также отправляя по одной строке за раз. Для чтения файла данных будем использовать вспомогательную функцию. В лис­ тин­ге 10.8 показан код, используемый для этого. Листинг 10.8  Чтение HTML и файла данных # Read HTML from file and send to client a row at a time. def send_html_file(filename, client): html = open(filename, 'r') for row in html: client.send(row) html.close() # Send the sensor data to the client. def send_sensor_data(client, filename): send_html_file("project3/part1.html", client) log_file = open(filename, 'r') for row in log_file: cols = row.strip("\n").split(",") # split row by commas # build the table string if all parts are there if len(cols) >= 5: html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2], cols[3], cols[4]) # send the row to the client client.send(html) time.sleep(0.50) log_file.close() send_html_file("project3/part2.html", client)
Написание кода  341 Импорт Импорт, который нам нужен для проекта, включает в себя импорт для классов select, sys, socket, Pin, SoftI2C, RTC (DS1307), SoilMoisture и ReadEvent. Полный список импорта показан ниже. Для того чтобы продолжить, откройте новый файл и назовите его plants.py. import network, select, socket, sys, time from machine import Pin, SoftI2C from project3.ds1307 import DS1307 from project3.soil_moisture import SoilMoisture from project3.read_timer import ReadEvent Нам также нужны две константы. Во-первых, нам нужна строка для имени log-файла, во-вторых, строка-образец, которую мы можем использовать для создания строк таблицы. HTML-код, который находится перед этой строкой, сохраняется в файлах, как описано ранее. Ниже показаны нужные константы. Обратите внимание, что мы используем синтаксис замены, чтобы можно было использовать функцию format() для заполнения деталей: # Constants DATA_FILENAME = 'plant_data.csv' # HTML web page for the project HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</ td><td>{4}</td></tr>" Функция main HTML-серверная часть функции main() аналогична предыдущему примеру, но вместо обработки запросов формы мы по умолчанию отправляем вебстра­ницу обратно клиенту. Единственная поддерживаемая команда – это команда CLEAR_LOG, которую необходимо указать в URL-адресе клиента, как описано ранее. Еще одно отличие заключается в том, что вместо размещения кода в глобальном разделе файла кода (чтобы он выполнялся при импорте файла в нашу консоль REPL или в файл plants.py) мы используем функции для подключения к сети, настройки RTC и отправления HTML-кода клиенту. Это увеличение сложности, которое вам следует начать использовать в качестве общей практики. Мы не видели этого в более ранних проектах, поэтому могли сосредоточиться на завершении кода. При написании собственных проектов обязательно используйте функции для хранения кода и вызывайте функции из другого кода. Поскольку это отличается от предыдущего проекта, мы рассмотрим код. В лис­тин­ге 10.9 показан код функции main(). Как и в предыдущих главах, нам необходимо добавить специальный код для Pico и Nano RP2040. Инструкции по изменению настроек SoftI2C для каждой платы см. в комментариях в тексте, а за более подробной информацией обратитесь к главам 8 и 9.
342  Проект: мониторинг растений Листинг 10.9  Функция main() мониторинга растений # Main function def main(): # Setup the socket and respond to HTML requests # Connect to the network if not connect(): sys.exit(-1) # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # NOTE: We only need to set the datetime once. Uncomment these # lines only on the first run of a new RTC module or # whenever you change the battery. rtc = DS1307(i2c) # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,23,16,15,30,2) # # Setup the sensors sensor_list = [ { # Arduino Nano RP2040 Connect and Raspberry Pi Pico # use pin=27, power=20 'pin': 27, 'power': 20, 'location': 'Green ceramic pot on top shelf', 'nick': 'Ivy', }, { # Arduino Nano RP2040 Connect and Raspberry Pi Pico # use pin=28, power=21 'pin': 28, 'power': 21, 'location': 'Fern on bottom shelf', 'nick': 'Fern', } ] # Setup the soil moisture object instance from the SoilMoisture class plants = SoilMoisture(rtc, DATA_FILENAME, sensor_list) # Setup the socket and wait for connections
Написание кода  343 addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] sock = socket.socket() sock.bind(addr) sock.listen(5) # Setup read event handler data_read_event = ReadTimer(120000) print("Ready for connections...") while True: # Since the socket.accept() method is blocking, the easiest way to # make it non-blocking is to use the select module which provides # functions to efficiently wait for events on multiple streams. # See https://docs.micropython.org/en/latest/library/ select.html # for more details. read_event, write_event, error = select.select((sock,), (), (), 1) if read_event: for readable in read_event: try: client, address = sock.accept() except OSError as err: time.sleep(0.50) continue print("Got a connection from a client at: %s" % str(address)) request = client.recv(1024) # Allow for clearing of the log but be careful! # The auto refresh will resend this command if you do not # clear it from your URL line. if (request[:14] == b'GET /CLEAR_LOG'): print('Requesting clear log.') plants.clear_log() else: send_sensor_data(client, DATA_FILENAME) client.close() if data_read_event.time_to_read(): data_read_event.reset() print("Reading data...") plants.read_sensors() time.sleep(1) sock.close() Изучите этот код. Обратите внимание, как мы реализуем функцию более модульным способом. Размещение общего кода в функциях не только помогает разбить проблему на части, но также делает ваш основной код (функцию main()) короче. Обратите также внимание на использование класса select. Это специальный класс, который позволяет нам использовать класс сокета, который является формой класса потока, без блокировки (т.е. без ожидания клиента перед возвратом). См. https://docs.micropython.org/en/latest/library/select.html для получения дополнительной информации об использовании класса select. Давайте посмотрим на готовый код.
344  Проект: мониторинг растений Полный код Теперь, когда мы рассмотрели все части модуля кода, давайте посмотрим на готовый код. В лис­тин­ге 10.10 показан полный код модуля Plant Monitor. Напоминаем, что можем сохранить этот файл как plant.py. Обратите внимание, что у нас также есть код для Wi-Fi, который мы использовали в предыдущих главах. Обязательно установите SSID и пароль для локальной настройки Wi-Fi. Листинг 10.10  Полный код модуля Plant Monitor1 # # MicroPython for the IoT Second Edition - Chapter 10 # # Chapter 10 - Plant Monitoring Web Server # # Required Components: # - (N) Soil moisture sensors (one for each plant) # # Imports for the project import network, select, socket, sys, time from machine import Pin, SoftI2C from project3.ds1307 import DS1307 from project3.soil_moisture import SoilMoisture from project3.read_timer import ReadTimer # Constants DATA_FILENAME = 'plant_data.csv' # HTML web page for the project HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>" # Setup the board to connect to our network. def connect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect('SSID', 'PASSWORD') while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect...") time.sleep(1) if not wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(wlan.ifconfig()[0])) return True # Read HTML from file and send to client a row at a time. 1 Обращаем внимание, что эта программа рассчитана по умолчанию на выполнение на Pico, строки для Nano RP2040 закомментированы. – Прим. перев.
Написание кода  345 def send_html_file(filename, client): html = open(filename, 'r') for row in html: client.send(row) html.close() # Send the sensor data to the client. def send_sensor_data(client, filename): send_html_file("project3/part1.html", client) log_file = open(filename, 'r') for row in log_file: cols = row.strip("\n").split(",") # split row by commas # build the table string if all parts are there if len(cols) >= 5: html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2], cols[3], cols[4]) # send the row to the client client.send(html) time.sleep(0.50) log_file.close() send_html_file("project3/part2.html", client) # Main function def main(): # Setup the socket and respond to HTML requests # Connect to the network if not connect(): sys.exit(-1) # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # NOTE: We only need to set the datetime once. Uncomment these # lines only on the first run of a new RTC module or # whenever you change the battery. rtc = DS1307(i2c) # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,23,16,15,30,2) # # Setup the sensors sensor_list = [ {
346  Проект: мониторинг растений # Arduino Nano RP2040 Connect and Raspberry Pi Pico # use pin=27, power=20 'pin': 27, 'power': 20, 'location': 'Green ceramic pot on top shelf', 'nick': 'Ivy', }, { # Arduino Nano RP2040 Connect and Raspberry Pi Pico # use pin=28, power=21 'pin': 28, 'power': 21, 'location': 'Fern on bottom shelf', 'nick': 'Fern', } ] # Setup the soil moisture object instance from the SoilMoisture class plants = SoilMoisture(rtc, DATA_FILENAME, sensor_list) # Setup the socket and wait for connections addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] sock = socket.socket() sock.bind(addr) sock.listen(5) # Setup read event handler data_read_event = ReadTimer(120000) print("Ready for connections...") while True: # Since the socket.accept() method is blocking, the easiest way to # make it non-blocking is to use the select module which provides # functions to efficiently wait for events on multiple streams. # See https://docs.micropython.org/en/latest/library/ select.html # for more details. read_event, write_event, error = select.select((sock,), (), (), 1) if read_event: for readable in read_event: try: client, address = sock.accept() except OSError as err: time.sleep(0.50) continue print("Got a connection from a client at: %s" % str(address)) request = client.recv(1024) # Allow for clearing of the log but be careful! # The auto refresh will resend this command if you do not # clear it from your URL line. if (request[:14] == b'GET /CLEAR_LOG'): print('Requesting clear log.') plants.clear_log()
Выполнение кода  347 else: send_sensor_data(client, DATA_FILENAME) client.close() if data_read_event.time_to_read(): data_read_event.reset() print("Reading data...") plants.read_sensors() time.sleep(1) sock.close() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Уделите некоторое время изучению кода, чтобы убедиться, что вы понимае­ те, как он работает. Если не считать создания веб-сервера, код проще, чем в предыдущих примерах. Теперь давайте запустим этот проект! Подождите, а что насчет файла данных? Если вас интересует файл данных, вам не о чем беспокоиться. Код предназначен для создания файла, даже если он не существует. Ниже показаны образцы данных, которые вы можете использовать в своих тестах: 2023-09-23 15:42:05,1,99,dry,Small fern on bottom shelf on porch 2023-09-23 15:42:10,2,204,dry,Green pot creeper thing on floor in living room 2023-09-23 15:42:22,1,215,dry,Small fern on bottom shelf on porch 2023-09-23 15:43:11,2,89,dry,Green pot creeper thing on floor in living room Если вы хотите начать с примеров данных, то можете это сделать, но убедитесь, что они разделены запятыми, без пробелов и по одной строке данных в каждой строке. Выполнение кода Теперь самое интересное! У нас есть код, настроенный для считывания влагосодержания почвы с растений и отправки всех собранных датчиками данных клиенту. Напомним, нам нужно скопировать созданные нами файлы с кодом на плату. Вспомните, что на созданные нами модули код ссылается следующим образом: from project3.ds1307 import DS1307 from project3.soil_moisture import SoilMoisture from project3.read_timer import ReadTimer
348  Проект: мониторинг растений Это означает, что нам нужно создать на плате MicroPython каталог с именем project3 и скопировать туда эти файлы (soil_moisture.py и read_timer.py1). Напомним, что сделать это можно из Thonny. Нам также необходимо скопировать два HTML-файла (part1.html и part2.html) в эту папку вместе с модулем RTC (ds1307.py), который мы использовали в главе 8. Наконец, мы можем скопировать основной код (plants.py) в корневую папку на плате MicroPython. Когда все файлы скопированы, вы должны увидеть файловую структуру, аналогичную той, что показана на рис. 10.5. Рис. 10.5  Пример файловой структуры для проекта мониторинга растений Скопируйте файлы, как показано на рисунке, и вы готовы протестировать проект. Все, что нам сейчас нужно, – это IP-адрес этой платы, на который можно указать нашему веб-браузеру. Мы можем получить его из операторов отладки, запустив код. В лис­тин­ге 10.11 показан начальный запуск проекта. Waiting to connect... Waiting to connect... Waiting to connect... Waiting to connect... Waiting to connect... Connected! My IP address is: 10.0.0.14 Soil moisture sensors are ready... Ready for connections... Reading data... Got a connection from a client at: ('10.0.0.13', 51754) Got a connection from a client at: ('10.0.0.13', 51755) 1 Здесь и далее автор, ссылаясь на файл read_timer.py, вероятно, имеет в виду файл read_event.py, содержащий класс ReadTimer (см. листинг 10.6). Но так как в коде и в описаниях имеются отсылки преимущественно к файлу read_timer.py (во всех случаях с пометкой «из главы 10»), то читателю предлагается самостоятельно разобраться в этой путанице с названиями файла модуля и класса. – Прим. перев.
А что дальше  349 Обратите внимание, что в этом случае IP-адрес – 10.0.0.14. Все, что нам нужно сделать, – это ввести его в браузер, результат показан на рис. 10.6. В зависимости от настроек Wi-Fi вы можете увидеть адрес в сети 192.168.X.X, и это нормально, если он соответствует той же подсети, что и ваш компьютер. Рис. 10.6  Проект мониторинга растений После ввода URL-адреса вы должны увидеть веб-страницу, похожую на показанное изображение. Если вы этого не получаете, обязательно проверьте HTML в своем коде, чтобы убедиться, что он точно такой, как показано; в противном случае страница может отображаться неправильно. ВНИМАНИЕ! Возможно, вы получите код ошибки EADDRINUSE, которая может возникнуть при многократном запуске кода. Это означает, что плата MicroPython обнаружила, что адрес используется и поэтому не будет инициализирован. В этом случае вы можете просто выключить (отсоединить) плату и снова включить ее (подключив обратно к USB-порту вашего ПК). Вы также должны убедиться, что сеть, к которой подключен ваш компьютер, может подключаться к сети, к которой подключена ваша плата. Если ваш домашний офис устроен так же, как мой, вы можете использовать несколько сетей Wi-Fi. Лучше всего, если ваша плата и ваш компьютер находятся в одной сети и подсети. На этом этапе вы завершили еще один настоящий IoT-проект MicroPython. В данном случае мы увидели проект IoT, который собирает и отображает данные. Отлично! А что дальше? Этот проект, как и предыдущий, показывает отличные перспективы для повторного использования методик в других проектах. Это особенно верно в отношении HTML-сервера. Если вам нравится просматривать данные своих датчиков через интернет, вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые
350  Проект: мониторинг растений вы можете рассмотреть. Некоторые просты, а иные могут оказаться сложной задачей или потребовать дополнительных исследований:   добавьте больше датчиков, чтобы расширить проект до большего количества растений;   добавьте датчик температуры для регистрации температуры окружающей среды и отображения ее на веб-странице;   перепишите HTML-код для создания строк JSON;   перепишите HTML-код для создания XML;   изучите HTML-код, чтобы изменить веб-страницу по своему вкусу. Рассмотрите возможность использования каскадных таблиц стилей для изменения фона кнопки при нажатии;   подключите свою плату к интернету и позвоните другу, чтобы тот подключился к вашей плате и опробовал ее;   добавьте на свою плату светодиоды, чтобы они светились, когда растения нуждаются в поливе. Конечно, если вы хотите перейти к следующему проекту, вы можете это сделать, но потратьте некоторое время на изучение этих потенциальных украшений – это будет хорошей практикой. Итоги Решения интернета вещей могут принимать разные формы. Одной из наиболее распространенных являются те, которые генерируют данные, которые мы можем просматривать через интернет (иногда называемые сборщиками данных). Реализация сборщиков данных может сильно различаться, но обычно они хранят данные в каком-то месте и предоставляют возможность их просмотра. Простейшими формами являются те, которые регистрируют данные и хранят их локально, на удаленном сервере, в базе данных или в облачной службе. Визуализация данных также может варьироваться от самого простого предоставления данных через веб-страницу. В этой главе мы рассмотрели IoT-проект MicroPython, который регистрирует данные, считанные с серии датчиков влажности почвы. Мы создали решение для мониторинга растений, сохранявшее данные на плате MicroPython. Проект также передавал данные через HTML-сервер, чтобы мы могли видеть их в любое время. Этот проект можно использовать в качестве шаблона для множества проектов по сбору данных. Вы можете просто следовать шаблону, описанному в этой главе, и создать свой собственный регистратор данных на основе HTML. В следующей главе мы увидим еще один проект, использующий веб-сервер. Мы построим небольшой проект, который поможет отслеживать влажность, барометрическое давление и температуру. Вперед!
Глава 11 Проект: мониторинг погоды Возможно, один из вездесущих проектов, которые вы найдете в интернете, – это простая метеостанция. Базовая метеостанция – отличный проект, потому что она показывает, какие возможности можно получить для измерения окружающей среды, и этот проект может быть интересен практически каждому. В конце концов, погода часто является темой практически любого вежливого социального взаимодействия. Если бы у вас была собственная метеостанция, особенно та, которую вы создали сами, вы могли бы внести немного больше в подобный разговор! В этой главе мы создадим базовую метеостанцию. Это не полноценная метеостанция, поскольку она не включает в себя некоторые наиболее часто используемые датчики, такие как скорость ветра и общее количество осадков (но вы можете их добавить после). Мы собираемся начать с основных наблюдений за температурой, влажностью и атмосферным давлением. Как и в предыдущей главе, мы будем реализовывать этот проект в виде веб-сервера, чтобы получить больше практики по взаимодействию с датчиками, регистрации данных и отображению результатов через веб-страницу. Поскольку в предыдущей главе мы рассмотрели настройку кода веб-сервера, этот проект будет сосредоточен на изменениях, а не на объяснении того, как работает веб-сервер. Если вы не работали над проектом из главы 10, возможно, вам захочется обратиться к разделу основного кода оттуда за подробностями о веб-сервере. Как вы увидите, код, используемый в этой главе, аналогичен коду из предыдущей главы, с некоторыми небольшими, но существенными изменения­ ми в том, как мы считываем данные с датчиков. Обзор В этой главе мы реализуем базовое решение для мониторинга погоды. Для этого потребуется использовать метеодатчик (датчик погоды), подключен-
352  Проект: мониторинг погоды ный к нашей плате MicroPython. Мы будем использовать потоки для чтения данных с датчиков и сохранения их в файле формата CSV. Многопоточность обеспечивает более быстрое и более надежное чтение. Теперь давайте посмотрим, какие компоненты нужны для этого проекта, а затем обсудим, как все соединить воедино. Необходимые компоненты В табл. 11.1 перечислены компоненты, которые вам потребуются. Таблица 11.1. Необходимые компоненты Компонент Кол-во Описание Ссылки BME280 1 Weather sensor www.sparkfun.com/products/13676 www.adafruit.com/product/2652 RTC модуль 1 Модуль RTC с резервной батареей1 www.sparkfun.com/products/12708 www.adafruit.com/product/3296 Перемычки 8 Перемычки «штырь/штырь», www.sparkfun.com/products/9838 6 или 7 дюймов www.adafruit.com/product/1950 Плата метеомодуля BME280 может поддерживать интерфейсы I2C или SPI. BME280 от SparkFun поддерживает оба варианта, что делает его идеальным для комплектации IoT-проекта2. На рис. 11.1 показан метеодатчик BME280 от SparkFun. Если у вас есть устройство от другого поставщика, убедитесь, что оно поддерживает I2C (или соответствующим образом измените код в этом проекте и схему подключения). В показанном датчике нет разъемов, поэтому вы можете добавить их самостоятельно (или попросите друга помочь вам). 1 2 См. сноску к табл. 8.1. – Прим. перев. Метеодатчик BME280 удобен тем, что предоставляет в одном модуле все необходимые величины: атмосферное давление, температуру и влажность воздуха. Однако если внимательно изучить описание самого датчика BME280 (компании Bosh), лежащего в основе платы, очевидно, что метрологические качества его как датчика температуры-влажности далеки от приемлемых – датчик измеряет температуру платы и влажность вокруг нее, которые могут быть очень далеки от параметров среды, окружающей прибор, из-за влияния расположенных рядом греющихся элементов (микроконтроллера, стабилизатора питания и пр.), к тому же, как правило, упакованных в непродуваемый корпус. Поэтому для получения более-менее достоверных показаний метеостанции рекомендуется использовать отдельно датчик атмосферного давления типа BMP280 (который можно ставить в непосредственной близости от платы контроллера) и отдельно датчик температуры-влажности, чувствительный элемент которого следует выносить за пределы корпуса прибора. Существует большой выбор таких датчиков, из которых наилучшими являются датчик SHT85 (фирмы Sensirion) и HYT 939 (Innovative Sensor Technology), а также более дешевые альтернативы: популярные датчики SHT20/21, SHT31 и др. – Прим. перев.
Настройка оборудования  353 Рис. 11.1  Плата датчика погоды (с разрешения sparkfun.com) К счастью, схема этого проекта менее сложна, чем в двух последних проектах. Давайте посмотрим, как соединить компоненты вместе. Настройка оборудования Настройка оборудования для этого проекта проще, чем в предыдущей главе. У нас есть только RTC и датчик BME280. Однако модуль RTC использует напряжение 5 В, а данная плата BME2801 – 3,3 В, поэтому мы будем использовать разные подключения питания. ПРЕДУПРЕЖ ДЕНИЕ Всегда проверяйте подключения питания, чтобы убедиться, что питание, подаваемое на устройство, соответствует его характеристикам. Питание датчика 3,3 В напряжением 5 В (или более) может привести к повреждению компонентов. Мы увидим, как подключить Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как вы увидите, соединения аналогичны, но места подключения к платам различаются. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 11.2 показаны подключения, необходимые для этого проекта для Raspberry Pi Pico. Обратите внимание, что мы подключаем RTC и BME280 к одним и тем же контактам для SDA и SCL (I2C). Это совершенно нормально и будет работать, поскольку каждое устройство на шине I2C имеет свой собственный адрес. На рис. 11.2 показана схема подключений для проекта на Pico. 1 Напряжение питания собственно датчика BME280 составляет 1,8–3,6 В, однако существуют модули с этим датчиком, работающие от 5 В. – Прим. перев.
354  Проект: мониторинг погоды Таблица 11.2. Соединения для проекта мониторинга погоды (Pico) Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на внешней плате 36 3V3 BME280 – 3.3V 38 GND BME280 – GND 11 GP8 BME280 – SDA 12 GP9 BME280 – SCL 40 VBUS RTC – VCC 38 GND RTC – GND 11 GP8 RTC – SDA 12 GP9 RTC – SCL Рис. 11.2  Подключения проекта мониторинга погоды (Pico) Соединения для Arduino Nano RP2040 Connect Поскольку мы используем те же компоненты для Arduino Nano RP2040 Connect, соединения будут аналогичными, но, как отмечалось в предыдущих главах, к Nano RP2040 подключения идут к другим выводам. Таким образом, некоторые связи будут отличаться. В табл. 11.3 показаны соединения, необходимые для Nano RP2040.
Настройка оборудования  355 Таблица 11.3. Соединения для проекта мониторинга погоды (Nano RP2040) Обозначение на плате Nano RP2040 (номер GPIO / функция) Вывод на внешней плате 3.3V BME280 – 3.3V GND BME280 – GND A4 (GP12. SDA) BME280 – SDA A5 (GP13, SCL) BME280 – SCL 5V RTC – VCC GND RTC – GND A4 (GP12. SDA) RTC – SDA A5 (GP13, SCL) RTC – SCL Обратите внимание, что большинство соединений одинаковы. Разница заключается в том, где они подключаются к плате контроллера. На рис. 11.3 показана схема подключения для проекта Nano RP2040. Рис. 11.3  Подключение проекта мониторинга погоды (Nano RP2040) Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте плату – необходимо немало обсуждений, прежде чем мы будем готовы протестировать проект.
356  Проект: мониторинг погоды Написание кода Представленный далее код может работать как на Pico, так и на Nano RP2040 Connect с небольшими изменениями, которые будут выявлены при обсуждении основной части кода. Как и в предыдущем примере, код длинный, поэтому разделим проект на части. Мы соберем все это перед тестированием проекта. Сначала напишем модуль метеодатчика. Код HTML-сервера и вспомогательные функции разместим в основном модуле. В отличие от проекта из предыдущей главы, здесь мы будем использовать потоки для одновременного выполнения двух частей проекта: основного кода и цикла чтения датчика с сохранением данных в log-файле. Поскольку эти две части проекта будут иметь доступ к log-файлу, откуда читается основной код и куда пишет модуль датчика, мы будем использовать специальную конструкцию для потоковой передачи, называемую блокировкой, которую мы можем применять для размещения функций чтения и записи с уверенностью, что только один поток получает доступ к критическим данным в данный момент времени. Эта концепция станет ясна, когда мы посмотрим на реализацию кода. Прежде чем мы приступим к коду, следует обсудить библиотеку (драйвер), которая понадобится для считывания данных с датчика BME280. Хорошей практикой является написать короткую тестовую программу для тестирования новых библиотек и датчиков. Давайте сделаем это в первую очередь. Тестирование модуля BME280 Библиотека, которую нам нужно использовать для чтения данных BME280, доступна на GitHub, и это библиотека, изначально предоставленная Adafruit, но с тех пор измененная. Вы можете найти и загрузить библиотеку по адресу https://github.com/gloveboxes/MicroPython-ESP8266-Environmental-Data-Streaming/blob/master/bme280.py. Вам следует загрузить ее на свой компьютер, так как он понадобится нам для загрузки кода на нашу плату MicroPython. Мы напишем простую программу с бесконечным циклом для чтения датчика, используя класс ReadTimer из главы 10. Беглый взгляд на документацию по библиотеке bme280.py показывает, что чтение с датчика можно выполнить после инициализации соединения I2C с переменными класса sensor.raw_values для необработанных значений и sensor.values для значений в читаемом формате следующим образом: i2c = SoftI2C(sda=sda, scl=scl, freq=100000) sensor = BME280(i2c=i2c, address=0x77) print("Raw Data ->", sensor.raw_values) print("Human Readable ->", sensor.values)
Написание кода  357 Обратите внимание на вызов инициализации BME280() (конструктор класса) и параметр – адрес устройства. Нам нужно передать этот параметр с указанным значением (0x77), поскольку в библиотеке настроен адрес по умолчанию 0x76, как показано ниже: # BME280 default address. BME280_I2CADDR = 0x76 Вы можете просто отредактировать библиотеку и установить для константы значение 0x77, но конструктор позволяет передавать адрес по значению. К сожалению, это не единственный сюрприз, который ждет нас от использования этой библиотеки. Нам также необходимо получить необработанные значения с датчика, но данная библиотека не предоставляет такой функции 1. Таким образом, нам придется отредактировать файл и добавить ее. В лис­ тин­ге 11.1 показана новая функция с именем raw_values, которую нам нужно добавить в библиотеку. Вы можете поместить эту функцию в конец файла bme280.py. Листинг 11.1  Новая функция read_value для библиотеки BME280 @property def raw_values(self): """ human readable values """ t, p, h = self.read_compensated_data() p = p // 256 pi = p // 100 pd = p - pi * 100 hi = h // 1024 hd = h * 100 // 1024 - hi * 100 return ("{}".format(t / 100), "{}.{:02d}".format(pi, pd), "{}.{:02d}".format(hi, hd)) Обратите внимание на декоратор2 @property. Это позволяет MicroPython рассматривать функцию как свойство, подобное свойству read_values. Удели1 2 Автор не приводит объяснений, зачем могут понадобиться необработанные (raw) данные с датчика, и далее они используются в этом проекте и в главе 14 только для иллюстрации, наряду с выводом физических величин. Обычно необработанные данные требуются в случае, если вы не доверяете библиотечным алгоритмам конвертации результатов измерений в физические величины и хотите подставить свои (полученные на основе независимой калибровки). Но тогда требуются именно сырые данные прямо с чувствительных элементов датчика, а не полученные обратным пересчетом из физических величин. Будем считать, что таким образом автор решил проиллюстрировать возможность их использования, хотя пример получился довольно искусственный и бесполезный на практике. – Прим. перев. Декоратор (decorator) – это обертка вокруг функций (или классов) в Python, которая меняет способ работы этих функций. Подробности см., например (на русском языке): https://habr.com/ru/companies/otus/articles/666016/. – Прим. перев.
358  Проект: мониторинг погоды те несколько минут прочтению кода, чтобы знать, как он работает. Вы можете сравнить его с функцией read_values в библиотеке. Необходимость добавления новых функций типична при поиске библио­ теки для своих проектов. Таким образом, это хороший пример того, как взять что-то почти то, что нам нужно, и соответствующим образом изменить это. Теперь давайте посмотрим на готовый тестовый код. В лис­тин­ге 11.2 показан код тестовой программы с именем test_bme280.py, которую вы можете использовать для проверки подключений к датчику BME280. Обратите внимание, что код написан как для Pico, так и для Nano RP2040 (просто добавьте/ удалите комментарии, как обсуждалось в предыдущих главах). Листинг 11.2  Тестовый код для BME280 # # MicroPython for the IoT Second Edition - Chapter 11 # # Chapter 11 - MicroPython Weather Web Server # # Test BME280 sensor with required modifications. # import network, socket, sys, time from machine import Pin, SoftI2C from project4.bme280 import BME280 from project4.read_timer import ReadTimer # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # Now, setup the sensor print("setting up sensor...") sensor = BME280(i2c=i2c, address=0x77) # Setup read event handler data_read_event = ReadTimer() while True: if data_read_event.time_to_read(): print("reading...", end="") data_read_event.reset() print("Raw Data ->", sensor.raw_values) print("Human Readable ->", sensor.values) time.sleep(1)
Написание кода  359 Чтобы запустить этот код, вам нужно будет создать на плате MicroPython папку с именем project4 и поместить в нее файлы bme280.py и read_timer. py (из главы 101). Затем вы можете скачать также файл кода test_bme280.py и запустить его. Когда вы запустите код, вы должны увидеть выходные данные, аналогичные листингу 11.3. Если что-то не работает, вернитесь назад и проверьте, правильно ли вы подключили модуль. В лис­тин­ге 11.3 показаны результаты выполнения этой тестовой программы. Листинг 11.3  Запуск тестового кода BME280 setting up sensor... reading...Raw Data -> ('16.89', '1001.69', Human Readable -> ('16.84C', '1001.73hPa', reading...Raw Data -> ('16.84', '1001.79', Human Readable -> ('16.83C', '1001.76hPa', reading...Raw Data -> ('16.84', '1001.82', Human Readable -> ('16.84C', '1001.80hPa', reading...Raw Data -> ('16.84', '1001.79', Human Readable -> ('16.83C', '1001.76hPa', reading...Raw Data -> ('16.84', '1001.74', Human Readable -> ('16.83C', '1001.75hPa', ... '65.00') '65.02%') '65.02') '65.02%') '65.00') '65.01%') '65.03') '65.02%') '65.04') '65.03%') Теперь, когда датчик BME280 протестирован, мы готовы создать модуль чтения датчика. Часть 1. Модуль чтения датчика Первая часть проекта будет заключаться в создании модуля, содержащего новый класс с именем WeatherSensor, который содержит все функции для чтения данных с датчика и сохранения данных в файл. В этом разделе мы продемонстрируем, как написать код модуля. Откройте новый файл и назовите его weather_sensor.py. Начнем с рассмотрения высокоуровневого дизайна. Высокоуровневый дизайн Как и в предыдущем примере, мы создадим функции для чтения данных с датчика и очистки log-файла. Однако этот модуль будет предназначен для предоставления функции обратного вызова, которая использует класс ReadTimer (тот же самый из главы 10) для периодического чтения данных с датчика. В табл. 11.4 показаны функции класса датчиков погоды. 1 См. сноску на стр. 348. – Прим. перев.
360  Проект: мониторинг погоды Таблица 11.4. Общее построение класса WeatherSensor (функции) Функция __init__() Параметры Описание csv_filename, i2c, rtc, rwlock Инициализация класса (конструктор) clear_log() Очистка log-файла _format_time() Форматирование времени для отображения и сохранение в log-файл read_sensor() Считать значения с датчика и сохранить их в log-файле run() value Запуск чтения датчика в потоке (функция обратного вызова) Обратите внимание на первую функцию с именем __init__(). Это конструктор класса, который будет вызываться из нашего основного кода при создании экземпляра класса. Обратите также внимание, что имена частных методов обозначаются одним подчеркиванием. В следующих разделах объясняются код инициализации и необходимые функции. Полный код мы увидим в конце. Настройка и инициализация В этом разделе мы обсудим код, который нам нужен для настройки и инициа­ лизации модуля. Во-первых, нам нужно кое-что импортировать, в том числе для модуля потоковой передачи, I2C, таймера и библиотек операционной системы. Нам также понадобятся библиотеки для классов BME280 и ReadTimer. В лис­тин­ге 11.4 показан код раздела настройки и инициализации. Поместите его в начало файла. Листинг 11.4  Настройка и инициализация класса WeatherSensor import _thread, time from machine import SoftI2C from project4.bme280 import BME280 from project4.read_timer import ReadTimer Конструктор Конструктор класса – это место, где происходит вся основная работа. Нам нужно выполнить несколько операций, в том числе следующие:   инициализировать все переменные класса;   настроить датчик BME280;   инициализировать таймер чтения. В лис­тин­ге 11.5 показан код конструктора класса. Листинг 11.5  Конструктор класса WeatherSensor # Constructor def __init__(self, csv_filename, i2c, rtc, rwlock):
Написание кода  361 self.csv_filename = csv_filename self.i2c = i2c self.rtc = rtc self.rwlock = rwlock # Now, setup the sensor self.sensor = BME280(i2c=i2c, address=0x77) time.sleep(0.100) # Setup read event handler self.data_read_event = ReadTimer(60000) print("Weather Monitor Client is ready.") Общедоступные (public) функции Общедоступных функций всего две. Первая, clear_log(), просто открывает файл для записи и закрывает его. Это эффективно очищает файл. Функция предусмотрена для удобства. Вторая функция, read_sensor(), считывает датчик и записывает данные в log-файл. Наконец, мы предоставляем функцию run() для запуска кода в потоке. Приватные (private) функции Функция _format_time() – это та же функция, которую мы использовали в главе 10. Напомним, она получает текущее время и форматирует время для записи в log-файл. Полный код Теперь, когда мы рассмотрели все части модуля, давайте посмотрим на готовый код. В лис­тин­ге 11.6 показан полный код модуля датчика погоды. Напоминаем, что мы сохраняем этот файл как weather_sensor.py. Листинг 11.6  Полный код модуля WeatherSensor # # MicroPython for the IoT Second Edition - Chapter 11 # # Chapter 11 - MicroPython Weather Web Server - BME280 Weather Class # # Imports for the project import _thread, time from machine import SoftI2C from project4.bme280 import BME280 from project4.read_timer import ReadTimer class WeatherSensor: """Sensor node using a BME280 sensor to read temperature, humidity, and barometric pressure.""" # Constructor
362  Проект: мониторинг погоды def __init__(self, csv_filename, i2c, rtc, rwlock): self.csv_filename = csv_filename self.i2c = i2c self.rtc = rtc self.rwlock = rwlock # Now, setup the sensor self.sensor = BME280(i2c=i2c, address=0x77) time.sleep(0.100) # Setup read event handler self.data_read_event = ReadTimer(60000) print("Weather Monitor Client is ready.") # Format the time (epoch) for a better view def _format_time(self): # Get datetime dt = self.rtc.datetime # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) return "{0:02}/{1:02}/{2:04} {3:02}:{4:02}:{5:02}".format( dt[1], dt[2], dt[0], dt[3], dt[4], dt[5]) # Clear the log def clear_log(self): if self.rwlock.acquire(): print("Lock available for clearing log!") return # skip the read log_file = open(self.csv_filename, 'w') log_file.close() self.rwlock.release() # Reads the sensor data def read_sensor(self): time.sleep_ms(50) raw_values = self.sensor.raw_values temperature = raw_values[0] humidity = raw_values[2] pressure = raw_values[1] print("Acquiring lock...") # Wait for the lock while not self.rwlock.acquire(): print("Waiting on lock for writing...") time.sleep(1) print("Writing...") data_file = open(self.csv_filename, "a") message = ("{0},{1},{2},{3}" "".format(self._format_time(), temperature, humidity, pressure)) data_file.write("{0}\n".format(message)) data_file.close() self.rwlock.release() print("Lock released.") print("Data read:", self.sensor.values) # Run the sensor read in a thread
Написание кода  363 def run(self): while True: if self.data_read_event.time_to_read(): self.data_read_event.reset() self.read_sensor() time.sleep(1) Уделите некоторое время изучению кода, пока не поймете все его части. Не беспокойтесь о функции run() и о том, как она вызывается. Мы увидим, как это сделать в основном коде. Часть 2. Чтение кода таймера Мы будем использовать таймер чтения из главы 10 без изменений. Вы можете скопировать файл напрямую, но обязательно измените константу, указывающую, как часто вы хотите считывать показания датчика. Это всегда критический момент, который следует учитывать, избегая чтения слишком большого количества данных, которые не меняются очень часто. Более подробную информацию см. в разделе о коде таймера чтения в главе 10. Хорошо, теперь мы готовы взглянуть на основной код. Часть 3. Основной код Модуль main будет использовать файл для хранения HTML-кода (поскольку он не меняется) и одну HTML-строку для заполнения HTML-таблицы данными из файла. Мы также добавим код для чтения даты и времени из RTC. Мы можем вручную отформатировать команду вместе с URL-адресом. Можно использовать этот метод, чтобы команды выполнялись без использования кнопок или других функций пользовательского интерфейса. Это также помогает усложнить применение этих команд, чтобы предотвратить чрезмерное использование. Например, мы можем предоставить команду очистки log-файла. Мы бы использовали URL-адрес типа http://192.168.42.140/CLEAR_ LOG, который отправляет запрос GET на HTML-сервер. Мы можем перехватить эту команду и очистить log-файл при ее выполнении1. В следующих разделах объясняются код инициализации и необходимые функции. Полный код мы увидим в конце. Начнем с HTML-кода. HTML-код (файлы) Файлы HTML аналогичны файлам, использованным в главе 10. Напомним, что первый файл (с именем part1.html) будет содержать код HTML вплоть до строк таблицы; второй файл (с именем weather_data.csv) будет заполнен 1 См. также предупреждение по поводу этой команды в разделе «Часть 3: основной код» главы 10. – Прим. перев.
364  Проект: мониторинг погоды классом WeatherSensor; а третий (с именем part2.html) будет содержать оставшийся HTML-код. Единственная разница между html-файлами этого проекта и главой 10 – это заголовок, имена столбцов и строки файла part1.html, как показано в лис­тин­ге 11.7. Файл part2.html такой же, как в главе 10, а файл weather_data.csv создан классом WeatherSensor. Листинг 11.7  HTML-код (part1.html) <!DOCTYPE html> <html> <head> <title>MicroPython for the IoT 2nd Edition - Weather Sensor</title> <meta http-equiv="refresh" content="30"> <style> table, th, td { border: 1px solid black; border-collapse: collapse; } th, td { padding: 5px; } th { text-align: left; } </style> </head> <center><h2>MicroPython for the IoT 2nd Edition - Weather Sensor</h2> </center><br> <center>A simple project to demonstrate how to retrieve sensor data over the Internet.</center> <center><br><b>Weather Data</b><br><br> <table style="width:75%"> <col width="180"> <col width="120"> <col width="100"> <col width="100"> <tr><th>Datetime</th><th>Temperature</th><th>Humidity</th><th> Pressure</th></tr> См. главу 10 для получения более подробной информации об этих файлах и о том, как они используются в основной функции. Импорт Импорт, который нам нужен для проекта, включает в себя импорт для классов select, sys, socket, Pin, SoftI2C, RTC (DS1307) и класса WeatherSensor. Полный список импорта показан ниже. Для того чтобы продолжить, откройте новый файл и назовите его weather.py. import network, select, socket, sys, time, _thread from machine import Pin, SoftI2C
Написание кода  365 from project4.ds1307 import DS1307 from project4.weather_sensor import WeatherSensor Нам также нужны две константы. Во-первых, нам нужна строка для имени log-файла, во-вторых, строка-образец, которую мы можем использовать для создания строк таблицы данных. HTML-код, который находится перед этой строкой, сохраняется в файлах, как описано ранее. Ниже показана используемая строка. Обратите внимание, что мы применяем синтаксис замены, чтобы можно было использовать функцию format() для заполнения деталей. # Constants DATA_FILENAME = 'weather_data.csv' # HTML web page for the project HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td></tr>" Функция main HTML-серверная часть функции main() аналогична предыдущему проекту, но вместо обработки запросов формы мы по умолчанию отправляем вебстраницу обратно клиенту. Единственная поддерживаемая команда – это команда CLEAR_LOG, которую необходимо указать в URL-адресе клиента, как описано ранее. В лис­тин­ге 11.8 показан код функции main(). Как и в предыдущих главах, нам необходимо добавить специальный код для Pico и Nano RP2040. Инструкции по изменению настроек SoftI2C для каждой платы см. в комментариях в списке, а за более подробной информацией обратитесь к главам 8 и 9. Листинг 11.8  Функция main() мониторинга погоды # Main function def main(): # Setup the socket and respond to HTML requests # Connect to the network if not connect(): sys.exit(-1) # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # NOTE: We only need to set the datetime once. Uncomment these # lines only on the first run of a new RTC module or # whenever you change the battery.
366  Проект: мониторинг погоды rtc = DS1307(i2c) # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,23,16,15,30,2) # # Read/Write lock rwlock = _thread.allocate_lock() # Setup the weather sensor weather = WeatherSensor(DATA_FILENAME, i2c, rtc, rwlock) # Start the weather sensor in a thread weather_thread = _thread.start_new_thread(weather.run, ()) # Setup the socket and wait for connections addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] sock = socket.socket() sock.bind(addr) sock.listen(5) print("Ready for connections...") while True: try: client, address = sock.accept() except OSError as err: time.sleep(0.50) continue print("Got a connection from a client at: %s" % str(address)) request = client.recv(1024) # Allow for clearing of the log but be careful! The auto refresh # will resend this command if you do not clear it from your URL line. if (request[:14] == b'GET /CLEAR_LOG'): print('Requesting clear log.') weather.clear_log() else: if not rwlock.acquire(): print("Lock unavailable for reading!") continue # skip the read send_sensor_data(client, DATA_FILENAME) rwlock.release() client.close() time.sleep(1) sock.close() weather_thread.exit() Найдите время, чтобы разобраться в этом коде. Обратите внимание, как мы реализуем функцию модульным способом. Размещение общего кода в функциях не только помогает разбить проблему на части, но также делает ваш основной код короче. Обратите снова внимание на использование класса select. Напоминаем, что это специальный класс, позволяющий нам использовать класс socket, который является формой класса потока, без блокировки (ожидания клиента перед возвратом). См. https://docs.micropython.org/en/latest/library/select.html для получения дополнительной информации об использовании класса select.
Написание кода  367 Код потока Код потоковой передачи очень короткий. Мы просто создаем блокировку, которую будем использовать для управления доступом к функциям чтения и записи файла журнала, и запускаем метеодатчик в другом потоке, предоставляя функцию run() указанным ниже образом. Когда код выполнен, мы останавливаем поток. rwlock = _thread.allocate_lock() # Start the weather sensor in a thread weather_thread = _thread.start_new_thread(weather.run, ()) ... weather_thread.exit() Потоки – довольно сложный материал, но все оказывается проще, при условии что вы осторожны с блокировками и вам не нужно вызывать другой поток. Дополнительную информацию о потоках см. на странице https://docs. micropython.org/en/latest/library/_thread.html. Давайте теперь посмотрим на полный код. Полный код Теперь, когда мы рассмотрели все части модуля кода, давайте посмотрим на готовый код примера. В лис­тин­ге 11.9 показан полный код модуля кода монитора погоды. Напомним, что мы можем сохранить этот файл как weather. py. Обратите внимание, что у нас также есть код Wi-Fi, который мы использовали в предыдущих главах. Обязательно установите SSID и пароль для локальной настройки Wi-Fi. Листинг 11.9  Основной код мониторинга погоды # # MicroPython for the IoT Second Edition - Chapter 11 # # Chapter 11 - MicroPython Weather Web Server # # Required Components: # - (1) BME280 Weather Sensor # # Imports for the project import network, select, socket, sys, time, _thread from machine import Pin, SoftI2C from project4.ds1307 import DS1307 from project4.weather_sensor import WeatherSensor # Constants DATA_FILENAME = 'weather_data.csv' # HTML web page for the project HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td></tr>"
368  Проект: мониторинг погоды # Setup the board to connect to our network. def connect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect('SSID', 'PASSWORD') while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect...") time.sleep(1) if not wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(wlan.ifconfig()[0])) return True # Read HTML from file and send to client a row at a time. def send_html_file(filename, client): html = open(filename, 'r') for row in html: client.send(row) html.close() # Send the sensor data to the client. def send_sensor_data(client, filename): send_html_file("project4/part1.html", client) try: log_file = open(filename, 'r') except OSError as e: print("Log file not found. Creating new file.") log_file = open(filename, 'w') log_file.close() log_file = open(filename, 'r') for row in log_file: cols = row.strip("\n").split(",") # split row by commas # build the table string if all parts are there if len(cols) >= 4: html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2], cols[3]) # send the row to the client client.send(html) time.sleep(0.05) log_file.close() send_html_file("project4/part2.html", client) # Main function def main(): # Setup the socket and respond to HTML requests # Connect to the network if not connect(): sys.exit(-1) # # I2C for the RTC # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12)
Написание кода  369 #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # # NOTE: We only need to set the datetime once. Uncomment these # lines only on the first run of a new RTC module or # whenever you change the battery. rtc = DS1307(i2c) # (year, month, day, hours. minutes, seconds, weekday: integer: 0-6 ) # rtc.datetime = (2023,09,23,16,15,30,2) # # Read/Write lock rwlock = _thread.allocate_lock() # Setup the weather sensor weather = WeatherSensor(DATA_FILENAME, i2c, rtc, rwlock) # Start the weather sensor in a thread weather_thread = _thread.start_new_thread(weather.run, ()) # Setup the socket and wait for connections addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] sock = socket.socket() sock.bind(addr) sock.listen(5) print("Ready for connections...") while True: try: client, address = sock.accept() except OSError as err: time.sleep(0.50) continue print("Got a connection from a client at: %s" % str(address)) request = client.recv(1024) # Allow for clearing of the log but be careful! The auto refresh # will resend this command if you do not clear it from your URL line. if (request[:14] == b'GET /CLEAR_LOG'): print('Requesting clear log.') weather.clear_log() else: if not rwlock.acquire(): print("Lock unavailable for reading!") continue # skip the read send_sensor_data(client, DATA_FILENAME) rwlock.release() client.close() time.sleep(1) sock.close()
370  Проект: мониторинг погоды weather_thread.exit() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Уделите некоторое время прочтению кода, чтобы убедиться, что вы понимаете, как он работает. Теперь давайте запустим этот проект! Выполнение кода Итак, у нас есть код, настроенный для считывания данных датчиков погоды и отправки всех собранных данных датчиков клиенту. Напомним, что нужно скопировать созданные нами файлы на нашу плату MicroPython. Вспомним из описания кода, что на созданные нами модули кода ссылаются из папки project4 следующим образом: from project4.ds1307 import DS1307 from project4.weather_sensor import WeatherSensor Напомним, что у нас также есть импорт в классе WeatherSensor: from project4.bme280 import BME280 from project4.read_timer import ReadTimer Рис. 11.4  Пример файловой структуры для проекта мониторинга погоды 1 См. сноску на стр. 348. – Прим. перев. Это означает, что нам нужно создать на плате MicroPython каталог с именем project4 и скопировать туда эти файлы (weather_sensor.py и read_timer.py1). Напомним, что сделать это можно с помощью Thonny. Нам также необходимо скопировать библиотеку BME280 (bme280.py) и два файла HTML (part1.html и part2.html) в эту папку вместе с модулем RTC (ds1307.py), который мы использовали в главе 8. Наконец, можно скопировать основной код (weather.py) в корневую папку на плате MicroPython.
Выполнение кода  371 Когда все файлы скопированы, вы должны увидеть файловую структуру, аналогичную той, что показана на рис. 11.4. Скопируйте файлы, как указано выше, и вы готовы протестировать проект. Все, что нам сейчас нужно, – это IP-адрес этой платы, который можно указать нашему веб-браузеру. Мы можем получить его из операторов отладки, запустив код. В лис­тин­ге 11.10 показан начальный запуск проекта. Листинг 11.10  Запуск мониторинга погоды Waiting to connect... Waiting to connect... Waiting to connect... Waiting to connect... Waiting to connect... Waiting to connect... Connected! My IP address is: 10.0.0.14 Weather Monitor Client is ready. Ready for connections... Acquiring lock... Writing... Lock released. Data read: ('17.26C', '1002.43hPa', '65.83%') Got a connection from a client at: ('10.0.0.13', Got a connection from a client at: ('10.0.0.13', Got a connection from a client at: ('10.0.0.13', Got a connection from a client at: ('10.0.0.13', Acquiring lock... Writing... Lock released. Data read: ('17.05C', '1002.48hPa', '65.75%') Got a connection from a client at: ('10.0.0.13', Got a connection from a client at: ('10.0.0.13', Got a connection from a client at: ('10.0.0.13', 52937) 52935) 52936) 52943) 52950) 52951) 52952) Обратите внимание, что в этом случае IP-адрес – 10.0.0.14. Все, что нам нужно сделать, – это поместить его в браузер, результат показан на рис. 11.5. В зависимости от настроек Wi-Fi вы можете увидеть адрес в сети 192.168.X.X, и это нормально, если он соответствует той же подсети, что и ваш компьютер. После ввода URL-адреса вы должны увидеть веб-страницу, похожую на показанное изображение. Если вы этого не получаете, обязательно проверьте HTML в своем коде, чтобы убедиться, что он именно такой, как показано; в противном случае страница может отображаться неправильно. ВНИМАНИЕ! Повреждение хранилища файлов на плате MicroPython происходит редко, но это возможно. Если вы видите странные данные или файлы неполные либо всегда пустые, возможно, ваша файловая система повреждена. Чтобы устранить эту проблему, перезагрузите другой образ .uf2. Например, попробуйте https://github.com/ dwelch67/raspberrypi-pico/blob/main/flash_nuke.uf2, а затем переустановите правильный образ системы, чтобы полностью обновить флеш-память.
372  Проект: мониторинг погоды Рис. 11.5  Проект мониторинга погоды Вы также должны убедиться, что сеть, к которой подключен ваш компьютер, может подключаться к сети, к которой подключена ваша плата. Если ваш домашний офис устроен так же, как мой, вы можете использовать несколько сетей Wi-Fi. Лучше всего, если ваша плата и ваш компьютер находятся в одной сети (и подсети). На этом этапе вы завершили еще один настоящий IoT-проект MicroPython. А что дальше? Этот проект, как и предыдущий, дает отличные перспективы для использования методик в других проектах. Это особенно верно для аспекта HTMLсервера. Если вам нравится просматривать данные своих датчиков через интернет, вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые вы можете рассмот­ реть. Некоторые из них просты, а некоторые могут оказаться сложной задачей или потребовать дополнительных исследований:   добавьте больше датчиков, чтобы расширить свой проект и включить в него больше погодных явлений;   перепишите HTML-код для создания строк JSON;   перепишите HTML-код для создания XML;   изучите HTML-код, чтобы изменить веб-страницу по своему вкусу. Рассмотрите возможность использования каскадных таблиц стилей для изменения фона кнопки при нажатии;   подключите свою плату к интернету и позвоните другу, чтобы тот подключился к вашему проекту и опробовал ее. Потратьте некоторое время на изучение этих потенциальных доработок – это будет хорошей практикой.
Итоги  373 Итоги В этой главе мы увидели еще один IoT-проект MicroPython, который регист­ рирует данные, считанные с метеодатчика. Мы создали решение для мониторинга погоды, которое сохраняло данные на локальной плате MicroPython. Проект также передавал данные через HTML-сервер, чтобы мы могли увидеть данные в любое время. Этот проект можно использовать в качестве шаблона для множества проектов по сбору данных. Вы можете следовать шаблону, описанному в данной главе, и создать свой собственный регистратор данных на основе HTML. В следующей главе мы приступим к теме облачных вычислений и обсудим, что доступно потребителям для создания IoT-проектов. Вслед за этой вводной главой мы познакомимся с тремя самыми популярными облачными сервисами интернета вещей, подняв три наших предыдущих IoT-проекта на новый уровень с их помощью.
Глава 12 Облачные вычисления На данный момент мы узнали, что Raspberry Pi Pico и Arduino Nano RP2040 Connect – отличные небольшие микроконтроллеры, обладающие большой мощностью в маленьком объеме. Они недороги, и их легко программировать, и теперь, когда мы накопили хороший опыт работы с базовой электроникой, включая то, как писать код для использования датчиков, реагировать на входные воздействия (например, на кнопки) и отображать данные, а также как создать простое решение для веб-сервера, пришло время поднять наши знания интернета вещей на новый уровень. В предыдущих главах вы видели ряд проектов, от самых простых до более сложных; пришло время обсудить, как сделать ваши IoT-данные доступными для просмотра другими через облако. В частности, вы получите небольшое представление о возможностях популярных сервисов и решений облачных вычислений. Я говорю «небольшое представление», потому что невозможно охватить все решения, доступные для интернета вещей в сфере облачных сервисов, в одной главе. Опять же, это тот случай, когда вы немного узнаете о чем-то и увидите это на практике, что поможет вам начать работу. В этой главе мы рассказываем, что такое облако и как оно используется для IoT-решений. В главе также представлен краткий обзор популярных облачных систем для интернета вещей. Облако: не просто маркетинговый хайп? Не верьте всякой шумихе и рекламным разговорам о продуктах, в названии которых есть слово «облако». Услуги и ресурсы облачных вычислений должны быть доступны через интернет из любого места, по подписке (платно или бесплатно) и позволять вам потреблять, производить и делиться соответствующими данными. Также учтите тот факт, что для получения данных вам необходим доступ к облаку. Таким образом, у вас нет альтернативы, если услуга недоступна (или не работает). Поскольку представленные технологии уникальны по реализации (но прос­ ты по концепции), я даю сведения про то, как возможно построить аппаратное обеспечение и запрограммировать проект с минимальными усилиями.
Обзор  375 Обзор Если вы не живете в очень изолированном месте, вас засыпали разговорами об облаке и интернете вещей. Вы видели рекламу в журналах и по телевидению, читали о ней в других книгах или посещали семинар либо конференцию. Если вы еще не потратили время на изучение того, что означает облако, вам должно быть интересно, из-за чего весь этот шум. Что такое облако? Проще говоря1, «облако» – это имя, присвоенное службам, доступным через интернет. Это могут быть серверы, к которым вы можете получить доступ (работающие как виртуальные машины на более крупном сервере), системы, обеспечивающие доступ к определенному программному обеспечению или среде, или такие ресурсы, как диски либо IP-адреса, которые вы можете присоединить к другим ресурсам. Технологии, лежащие в основе облака, включают распределенную обработку данных, виртуализацию и сетевые технологии. Правильный научный термин – облачные вычисления. Хотя глубокое погружение в облачные вычисления выходит за рамки этой книги, этого достаточно, чтобы понять, что вы можете использовать службы облачных вычислений для хранения данных датчиков. Что такое облачные вычисления? К сожалению, термин «облачные вычисления» используется слишком часто и для некоторых стал маркетинговым термином. Настоящие решения для облачных вычислений – это услуги, которые предоставляются подписчикам (заказчикам) посредством сочетания виртуализации, распределенных вычислений (распределенной обработки и хранения) и средств для поддержки виртуализированного оборудования и программного обеспечения, таких как IP-адреса, которые привязаны к подписке, а не к физическому устройству. Таким образом, вы можете использовать и отбрасывать ресурсы на лету в соответствии с вашими потребностями. Стоимость этих ресурсов, услуг и функций определяется моделями использования (называемыми планами, или уровнями, подписки), при которых вы можете платить столько, сколько вам нужно. Например, если вам нужно больше вычислительной мощности, вы можете перейти на уровень подписки, который предлагает больше ядер ЦП, больше памяти и т. д. Таким образом, вы платите только за то, что вам нужно, а это означает, что организации потенциально могут значительно сэкономить на инфраструктуре. 1 Опытные знатоки облачных технологий скажут вам, что об облаке можно узнать гораздо больше. – Прим. авт.
376  Облачные вычисления Классическим примером этого преимущества является случай, когда организация выполняет короткую и интенсивную работу, требующую дополнительных ресурсов для поддержания ее продуктов и услуг. Используя облако, организации могут временно увеличить возможности своей инфраструктуры и, как только пик будет пройден, вернуться к нормальному состоянию. Это намного лучше, чем арендовать или покупать тонну оборудования для одного мероприятия. К сожалению, есть некоторые поставщики, предлагающие облачные решения, которые далеко не всегда являются полноценными. В большинстве случаев они представляют собой не что иное, как обычное интернет-хранилище с доступом. Пример настоящих облачных сервисов Microsoft Azure – это настоящее: полноценное решение для облачных вычислений с впечатляющим набором функций, поддерживающее практически любое облачное решение, о котором вы только можете мечтать. СОВЕТ Если вы хотите узнать больше об облачных вычислениях и их многочисленных аспектах, см. https://en.wikipedia.org/wiki/Cloud_computing (то же самое по-русски: https://ru.wikipedia.org/wiki/Облачные_вычисления). Как облако помогает интернету вещей? Хорошо, теперь, когда мы представляем, что такое облачные системы, как они могут помочь в проектах интернета вещей? Существует множество способов, но наиболее распространенными являются механизмы хранения и представления данных вместо их локального или даже удаленного хранения в другой системе, например на выделенном сервере базы данных. То есть вы можете отправлять данные, которые собираете со своих датчиков, в облако для хранения и даже использовать дополнительные облачные сервисы для просмотра данных с помощью диаграмм, графиков или просто текста. Облако – это предел того, как вы можете представить свои данные. Но хранение данных – не единственная функция, которую вы можете использовать в облаке. Существуют и другие сервисы, которые вы можете использовать для формирования решения. Например, большинство платных облачных систем интернета вещей предоставляют функции, которые могут «общаться» друг с другом, позволяя связать их вместе для быстрого создания решения. Эти функции часто называют компонентами, а не службами, но применяются оба термина. Например, в Microsoft Azure вы можете хранить свои данные с помощью одного из нескольких компонентов, а затем связывать их с другими, которые позволяют вам изменять данные с помощью запросов, другие – для маршрутизации данных в другие места (даже к другому поставщику облачных услуг) и к одному из нескольких компонентов для отображения данных. Это представляет собой набор этаких строительных блоков. Теперь, когда у нас есть общий обзор облачных систем, давайте посмотрим на те, которые напрямую поддерживают IoT-проекты.
Облачные системы интернета вещей (IoT Cloud)  377 Облачные системы интернета вещей (IoT Cloud) Существует ряд поставщиков облачных IoT-технологий, которые предлагают всевозможные продукты, возможности и функции, подходящие для всего, что вы можете придумать для проектов интернета вещей. Среди множества поставщиков, предлагающих такие решения, может быть сложно выбрать одного. Ниже приведен краткий список наиболее популярных предложений от ведущих поставщиков облачной индустрии:   Oracle IoT: www.oracle.com/internet-of-things/;   IoT-центр Microsoft Azure: https://azure.microsoft.com/en-us/product-categories/iot/;   Google IoT Core: https://cloud.google.com/iot-core;   IBM IoT: www.ibm.com/internet-of-things;   облако Arduino IoT: www.arduino.cc/en/IoT/HomePage;   Adafruit IO: https://io.adafruit.com/;   «If This Then That» (IFTTT): https://ifttt.com/;   MathWorks ThingSpeak: https://thingspeak.com/. Большинство поставщиков предлагают коммерческие продукты, но некоторые, такие как Google, Azure, Arduino, IFTTT и ThingSpeak, предлагают ограниченные бесплатные учетные записи. Некоторые из них вообще бесплатны, например Adafruit.IO и Arduino IoT Cloud, но могут ограничивать вас конкретной платформой или меньшим набором функций. Как вы можете догадаться, иные предложения представляют собой сложные решения, требующие сложного обучения, но предложения, например, IFTTT и ThingSpeak просты и удобны в использовании. Поскольку здесь нам нужно простое в использовании (и бесплатное!) решение, в следующей главе мы воспользуемся ThingSpeak, чтобы углубить знакомство с облачными системами интернета вещей. СОВЕТ Если вы хотите использовать одного из других поставщиков, обязательно внимательно прочитайте все руководства, прежде чем приступать к написанию кода. Давайте рассмотрим некоторые типы сервисов, доступных в облачных системах, поддерживающих IoT-проекты. Доступные облачные IoT-сервисы Проекты IoT предлагают потрясающую возможность расширить наши знания об окружающем нас мире и наблюдать за событиями со всего мира, где бы мы ни находились. Для реализации этих возможностей облачные службы интернета вещей предоставляют набор сервисов, которые вы можете использовать в своих приложениях.
378  Облачные вычисления Существуют сервисы для сбора данных, управления вашими устройствами, выполнения аналитики и даже расширения приложений и обработки, которыми вы можете воспользоваться. Например, некоторые поставщики включают в себя полное управление пользователями, где вы можете предоставить учетные записи пользователей, чтобы люди могли входить в систему, использовать ваше облачное решение и просматривать ваши данные. Ниже перечислены некоторые типы доступных услуг. Некоторые поставщики могут не предлагать все услуги, а услуга, общая для поставщиков, может работать по-разному у разных поставщиков. Однако это должно дать вам представление о том, какие услуги доступны, и общее представление о наборе функций.   Управление устройствами: позволяет настраивать, управлять и отслеживать устройства в вашей IoT-сети.   Хранение данных (Data Storage): позволяет хранить ваши IoT-данные либо во временном (обычно бесплатном на несколько дней), либо в постоянном (платном) хранилище.   Аналитика данных: позволяет выполнять анализ данных для поиска тенденций, выбросов или любой другой формы аналитического запроса.   Запросы и фильтры данных: вы можете выполнять запросы или фильт­ ровать данные после их отправки в облачную службу для подробного представления или преобразования.   Большие данные (big data): позволяют хранить огромные объемы данных и выполнять операции с ними.   Инструменты визуализации: различные информационные панели и графики, которые можно использовать для представления данных в осмысленном виде (электронные таблицы, круговые диаграммы и т. д.).   Высокая доступность: предоставляет функции, которые позволяют вам работать, даже если часть облачных серверов выходит из строя или отключается из-за проблем с сетью.   Сторонняя интеграция: позволяет подключать ваши IoT-службы к серверам от других поставщиков, например ваши данные на Adafruit IO к IFTTT для запуска SMS-сообщения.   Безопасность (данных и пользователей): обеспечивает поддержку управления учетными записями пользователей, безопасный доступ и многое другое для ваших приложений.   Шифрование: позволяет шифровать ваши данные либо в облаке, либо при передаче данных из одной службы в другую.   Развертывание: аналогично управлению устройствами, но в более широком масштабе: вы создаете IoT-устройства, используя общие профили, операционные системы, конфигурации и т. д.   Масштабируемость: возможность масштабирования от небольшого количества устройств и сервисов до сетей из множества устройств. Часто это доступно только в крупных платных сервисах поставщиков.   API (поддержка, программирование): позволяют писать код для прямого взаимодействия со службами вместо отправки веб-запросов. Часто как часть расширенных платных услуг поставщика.
Облачные системы интернета вещей (IoT Cloud)  379 В наших начальных IoT-проектах мы сосредоточимся на подмножестве этих услуг, которые можно сгруппировать в несколько категорий. Давайте рассмотрим несколько наиболее распространенных сервисов, которыми вы, возможно, захотите начать пользоваться прямо сейчас. Хранилища данных (Data Storage) Сервисы хранения данных позволяют сохранять ваши данные в облаке, а не на локальном устройстве. Некоторые данные, такие как оповещения или уведомления, хранить не требуется, и вам следует подумать о том, понадобятся ли вам конкретные разновидности данных в будущем и будут ли они зависеть от проекта. Например, если вы хотите создать проект оповещения о погоде, вам может быть все равно, какая температура была неделю или даже месяц назад. Однако если вы хотите сделать любительский прогноз погоды, вам потребуется хранить данные в течение некоторого времени (возможно, лет). Вы можете рассмотреть возможность локального хранения данных, что возможно для некоторых платформ, таких как Raspberry Pi, но Arduino и аналогичные платы имеют очень ограниченные возможности хранения. Таким образом, если вам необходимо хранить данные в течение определенного периода времени, а хранить их локально невозможно, вам следует учитывать это при выборе поставщика облачных услуг. Узнайте, как будут храниться данные, какие механизмы необходимы для отправки данных в службу и как получить данные из службы. Запросы и фильтры данных Эти службы позволяют выполнять запросы к данным, когда они передаются в облачные службы или через них. Возможно, вы захотите показать пользователям только часть данных или отфильтровать данные, чтобы данные с определенных устройств, дат и т. д. отображались для одного из нескольких представлений. Эти службы следует рассмотреть в случае IoT-проектов, которые собирают данные с нескольких датчиков или нескольких устройств, и данные хранятся в течение определенного периода времени. Например, если у вас есть устройства, географически распределенные по обширной территории, вам может потребоваться просмотреть данные только с подмножества этих устройств. Аналогично если у вас есть данные за несколько периодов времени, часов, дней и недель, вам может потребоваться просмотреть данные только за определенное время. Инструменты визуализации Эти службы, наряду с маршрутизацией и обменом сообщениями, чаще всего используются при запуске IoT-проектов. Это просто сервисы, которые позволяют получать данные в наглядной форме через интернет. Это может быть простой список данных или сложная информационная панель с элементами управления, которые пользователи могут использовать для управ-
380  Облачные вычисления ления отображением. К счастью, большинство поставщиков облачных услуг предоставляют различные инструменты (некоторые даже несколько разных), которые вы можете использовать для представления данных для себя или для пользователей. Маршрутизация и обмен сообщениями Эти сервисы являются сердцем или основой облака интернета вещей. Они объединяют в себе связующее звено, связывающее различные сервисы вмес­ те. Точнее, они предоставляют вам механизмы подключения устройств к сервисам, а эти сервисы – к другим сервисам, таким как запросы, фильтры и инструменты визуализации, что позволяет вам создать решение интернета вещей с использованием нескольких облачных сервисов. Что могут сделать облачные сервисы для наших проектов интернета вещей? До сих пор мы не обсуждали, как использовать данные, сгенерированные в наших проектах, кроме сохранения данных в файле на плате MicroPython. Из-за ограниченного размера хранилища на плате вы столкнетесь с проб­ лемами, которые необходимо решить: например, какой объем данных вы хотите хранить и как долго. Хотя это проблемы, которые можно решить, более важный вопрос заключается в том, что вы собираетесь делать с данными. Хотели бы вы увидеть, как данные изменяются с течением времени, как данные одного датчика сравниваются с другими, как часто меняется значение, или получить базовую статистику, такую как минимальные, максимальные и средние значения? Все эти вещи требуют вычислительной мощности, которая у Pico отсутствует. Кроме того, вы можете захотеть просмотреть данные в графическом представлении, в виде одного или нескольких графиков. Лучший способ сделать это – воспользоваться услугами IoT Cloud. Вы можете не только легко хранить данные, но также выполнять анализ данных и представлять их в одном из нескольких графических изображений. Давайте обсудим возможные действия с интернетом вещей в облачных вычислениях, прежде чем мы углубимся в несколько проектов. Начало работы с облачными вычислениями Существует множество поставщиков облачных услуг, предлагающих IoT-ре­ ше­ния для разработчиков, и каждый год их становится больше. Сложность
Начало работы с облачными вычислениями  381 и возможности некоторых провайдеров впечатляют и обычно выходят за рамки потребностей энтузиастов и любителей. Эти услуги являются платными, и их стоимость может со временем увеличиваться. К счастью, доступно несколько недорогих вариантов (некоторые из них бесплатны с ограничения­ ми по функциям), которые мы можем использовать, чтобы начать работу над проектами интернета вещей. В этом разделе представлены три поставщика облачных услуг, которые предлагают недорогие или бесплатные услуги. К ним относится использование протокола телеметрических сообщений (MQTT), который мы будем применять с Adafruit IO, Arduino IoT Cloud1 и ThingSpeak от MathWorks. Протокол сообщений телеметрии (MQTT) Первая технология, которую мы рассмотрим, – это механизм отправки сообщений другим компьютерам (службам) с использованием определенного протокола, который часто поддерживается специализированными программными интерфейсами. Один из самых простых в использовании с микроконтроллерами способов определяет протокол с ролями публикатора и подписчика. То есть вы можете публиковать данные (записывать) или подписываться на данные (читать) и даже получать уведомления о появлении новых данных. Один из подобных простых протоколов публикации/ подписки называется MQTT, что расшифровывается как (Message Queuing Telemetry Transport, передача телеметрии с очередью сообщений). Модель публикации/подписки существует уже некоторое время, по крайней мере в теории и концепции. Существуют также программные конструкции, реализующие роли в этом протоколе. Издатель (публикатор) публикует данные в месте (на сервере, базе данных или хранилище структурированных данных), которое позволяет подписчикам получать данные. Таким образом, издатели – писатели, а подписчики – читатели. В случае IoT-проектов у нас есть один или несколько сенсорных узлов, отправляющих данные в хранилище с использованием очереди сообщений, в которой записываются сообщения, содержащие данные. Когда подписчики подписываются на данные, они получают сообщения в том порядке, в котором они были отправлены, и анализируют сообщение на наличие данных. Таким образом, им не нужно добавлять уровень абстракции данных, подобный тем, которые мы использовали бы с сервером базы данных. В этом случае протокол MQTT – это все, что вам нужно. MQTT – это простой и очень легкий протокол (то есть для его использования не требуется огромная библиотека со сложным набором шагов), который вы можете использовать с платами MicroPython. Поскольку MQTT основан на очереди сообщений, протокол очень терпим к ненадежной доставке данных. А поскольку для использования не требуется много памяти, его можно 1 Не только для Ардуино! – Прим. авт.
382  Облачные вычисления применять на небольших устройствах. Это означает, что MQTT – это способ гарантировать, что ваши небольшие IoT-устройства смогут отправлять данные на сервер (называемый брокером) с разумной гарантией доставки – как для издателей, так и для подписчиков. Это делает MQTT идеальным инструментом для использования в проектах интернета вещей. Интересно, что MQTT существует с 1999 года. Его изобрели доктор Энди Стэнфорд-Кларк из IBM и Алрен Ниппер из Arcom (Eurotech). С тех пор он мало изменился и был адаптирован к растущему числу платформ. Дополнительную информацию о MQTT см. на http://mqtt.org/faq. Arduino IoT Cloud Вторая возможность, которую мы рассмотрим, – это Arduino IoT Cloud, иног­ да называемый просто Arduino Cloud, который предназначен для энтузиас­ тов и любителей исследовать возможности использования плат Arduino. Он позволяет использовать плату MicroPython (ту, которая на основе RP2040), а также многие другие платы Arduino. Облако Arduino IoT предназначено для того, чтобы максимально упрос­ тить начало работы с интернетом вещей в облаке благодаря быстрой настройке, упрощенным API-интерфейсам и мощным интерактивным опциям (называемым информационными панелями). Облако Arduino IoT также позволяет вам управлять вашими IoT-устройствами через интернет и даже предоставляет интерфейс с Amazon Alexa API. Правда, это круто? В сервисе используются такие понятия, как вещи (things1 – проекты интернета вещей), информационные панели (dashboards), устройства (devi­ ces – плата/платформа MicroPython) и т. д., представленные в упрощенном интерфейсе настройки, который может использовать каждый. Ниже приводится краткий список интересных функций, некоторые из которых доступны только через язык программирования Arduino, но большинство доступно и для проектов MicroPython. Да, и самое приятное то, что пользоваться этой услугой можно бесплатно!   Мониторинг данных: отслеживайте значения датчиков и отображайте их на информационной панели.   Синхронизация переменных: синхронизируйте переменные между устройствами.   Планировщик: запланируйте действия для запуска в определенное время.   Загрузка ПО по беспроводной сети (OTA2): обновление устройств по беспроводной сети. 1 2 Слово thing, обозначающее IoT-проект, зарегистрированный в Arduino IoT Cloud, образовалось как отсылка к термину «internet of thing» («интернет вещей»). – Прим. перев. Букв. Over-The-Air Uploads (OTA Uploads) – «загрузка по воздуху». – Прим. перев.
Начало работы с облачными вычислениями  383   Веб-перехватчики: создавайте «перехватчики» для подключения проектов к другим сервисам.   Поддержка Amazon Alexa: управляйте своим проектом с помощью голоса через Alexa.   Совместное использование панели управления: делитесь данными со всем миром. СОВЕТ См. https://docs.arduino.cc/arduino-cloud/getting-started/iot-cloud-getting-star­ ted для получения более подробной информации и о том, как начать работу с облаком Arduino IoT. Сервис ThingSpeak Другая служба, похожая на MQTT, позволяет хранить ваши данные в облаке с помощью популярной и простой в использовании облачной службы размещения IoT-данных от MathWorks под названием ThingSpeak (www.thingspeak. com). ThingSpeak предлагает бесплатную учетную запись для некоммерческих проектов, которые генерируют менее трех миллионов сообщений в год или около 8200 сообщений в день. Бесплатные учетные записи ограничены четырьмя каналами (канал эквивалентен проекту и может сохранять до восьми элементов данных). Если вам необходимо хранить или обрабатывать больше данных, вы можете приобрести коммерческую лицензию одной из четырех категорий; каждая из них имеет определенные продукты, функции и ограничения: Standard, Academic, Student и Home. Посетите https://thingspeak. com/prices и щелкните каждый из вариантов лицензии, чтобы узнать больше о функциях и ценах. ThingSpeak работает, получая сообщения от устройств, содержащих данные, которые вы хотите сохранить или построить на графике. Доступны биб­ лиотеки, которые вы можете использовать для определенных платформ или языков программирования, таких как Python или платформа Arduino. Однако вы также можете использовать протокол межмашинного подключения (M2M – полный аналог MQTT) или REST1 API, разработанные по принципу запроса–ответа, для обмена данными через HTTP. Вы даже можете читать свои данные с других устройств. СОВЕТ См. www.mathworks.com/help/thingspeak/channels-and-charts-api.html для получения более подробной информации о ThingSpeak MQTT и API REST. 1 https://en.wikipedia.org/wiki/Representational_state_transfer. – Прим. авт. REST (Representational State Transfer, репрезентативная передача состояния) – основанный Роем Филдингом (Roy Fielding) стиль взаимодействия компонентов распределенного приложения в сети. О принципах REST см. на русском языке https:// habr.com/ru/articles/590679/. – Прим. перев.
384  Облачные вычисления Если вы хотите читать или писать через канал ThingSpeak, то можете пуб­ ликовать сообщения MQTT, отправлять запросы через HTTP к REST API или использовать одну из библиотек для конкретной платформы, которые включают эти механизмы. Канал может иметь до восьми полей данных, представленных в виде строковых или числовых данных. Вы также можете обрабатывать числовые данные, используя несколько сложных процедур, таких как суммирование, усреднение, округление и т. д. Итоги В этой главе мы узнали больше об облачных системах и о том, как их можно использовать в проектах интернета вещей, и готовы модифицировать наши проекты для их использования. Однако здесь мы только прикоснулись к поверхности. С помощью простого бесплатного облачного решения можно сделать гораздо больше. В следующей главе мы расширим наш обзор облачных систем для интернета вещей, подробнее рассмотрев Arduino IoT Cloud – простой в использовании облачный сервис размещения IoT-данных от arduino.cc. Вы узнаете, как отправлять данные в облако и отображать их с помощью красивой и простой в использовании графики.
Глава 13 Arduino IoT Cloud Теперь мы начинаем то, что некоторые считают настоящей сутью проектов интернета вещей: внедрение облачных сервисов, чтобы сделать проекты доступными через интернет. До сих пор мы видели, как использовать упрощенный веб-сервер для отображения информации, но для этого обычно требуется сделать сеть, к которой подключается плата MicroPython, видимой (доступной) из интернета – ситуация, которую не все могут себе позволить, т. к. она получается довольно дорогой и сложной. Размещать, управлять или обслуживать устройство без дополнительных затрат – в этом и заключается полезность услуг облачных вычислений. В предыдущей главе мы кратко рассмотрели возможности создания недорогих решений интернета вещей, размещенных в облачных сервисах. В этом смысле хостинг означает, что мы используем одну или несколько услуг для реализации нашего IoT-проекта. Например, наиболее распространенными сервисами являются презентационные сервисы, которые позволяют нам создавать пользовательский интерфейс с поддержкой интернета. В этой главе мы возьмем наш проект из главы 9, пешеходный светофор, и продемонстрируем, как вы можете использовать облачные сервисы для управления вашей платой MicroPython. Правильно, мы заменим физическую кнопку в нашем проекте на кнопку в интернете. Облачный сервис, специально разработанный для такой функции, – это Arduino IoT Cloud от arduino.cc. Обзор Вспомним из предыдущей главы, что Arduino IoT Cloud – это простой в использовании сервис, предназначенный для проектов IoT. Таким образом, он предоставляет все необходимое для создания и размещения подобных проектов. Хотя сервис предназначен для работы с Arduino и Arduino-совместимых платформ, вы можете использовать его с любой платформой, для которой есть библиотека (драйвер). К счастью, нам доступно несколько библиотек Python.
386  Arduino IoT Cloud В следующем разделе представлено краткое руководство по началу работы с облаком Arduino IoT. Как вы увидите, работа с некоторыми функциями требует небольшой настройки, а иные компоненты и код не так интуитивно понятны, как в некоторых других облачных сервисах. Тем не менее проект, который мы реализуем, продемонстрирует возможности использования сервисов Arduino IoT Cloud в ваших будущих проектах. СОВЕТ Если вы хотите узнать больше об Arduino IoT Cloud и о том, как его использовать с платформой микроконтроллеров Arduino, см. онлайн-документацию по адресу https://docs.arduino.cc/arduino-cloud/getting-started/iot-cloud-getting-started. Давайте начнем работать с Arduino IoT Cloud. Начало работы с Arduino IoT Cloud В этом разделе мы узнаем, как начать применять Arduino IoT Cloud, включая некоторые его термины и инструменты, продемонстрированные с помощью простой тестовой программы, которую вы можете использовать, чтобы убедиться, что ваше соединение с Arduino IoT Cloud работает. Начинайте с малого – построите большое Не поддавайтесь искушению сразу перейти к комплексному решению, столкнувшись с инновационными технологиями. Вы обнаружите, что отладочный код, содержащий все ваши функции, которые не были протестированы заранее, разочаровывает. Лучший способ действий для любого сложного проекта – начать с малого и протестировать отдельные функции перед их включением. Это позволит вам собрать код из рабочих частей в единое целое с уже исправленными ошибками. Чтобы начать работу с Arduino IoT Cloud, вам необходимо создать учетную запись на arduino.cc. Затем вы можете начать создавать проекты (называемые вещами), которые содержат устройства (вашу плату интернета вещей), переменные (содержащие ваши данные) и информационную панель для отображения данных. Вкратце, сначала мы создаем устройство, представляющее нашу плату MicroPython, имеющее одну или несколько переменных, содержащих наши данные, затем добавляем вещь (проект) и в ней виджеты, связанные с переменными, для настройки пользовательского интерфейса. Опять же, эти концепции интуитивно понятны, но некоторые конфигурации и код не столь очевидны. Начнем с создания учетной записи.
Начало работы с Arduino IoT Cloud  387 Создание учетной записи Arduino Чтобы создать учетную запись, посетите arduino.cc и нажмите кнопку Sign In (Войти) в правом верхнем углу меню. ПРИМЕЧАНИЕ соединение. Вас могут попросить указать регион, в котором лучше всего настроить Щелкнув ссылку, вы увидите новую страницу, которая позволит вам войти в Arduino, если у вас уже есть учетная запись, или вы можете войти в систему, используя свою учетную запись Google, GitHub, Facebook или Apple 1. Если у вас нет учетной записи, вы можете нажать Create one (Создать), как показано на рис. 13.1. Рис. 13.1  Создать новый аккаунт Arduino После создания учетной записи снова нажмите Sign In (Войти) и укажите новую учетную запись и пароль. Вы увидите страницу, похожую на рис. 13.2. Нажмите Cloud (Облако), чтобы получить доступ к облачным сервисам. Рис. 13.2  Целевая страница входа в систему (arduino.cc) 1 Я не рекомендую эти варианты, но это ваш выбор. – Прим. авт.
388  Arduino IoT Cloud На следующей странице отображается общая страница сервисов Arduino IoT Cloud. Та часть, к которой мы хотим получить доступ, – это службы IoT, которые мы можем открыть, нажав Get Started (Начать), как показано на рис. 13.3. Рис. 13.3  Запуск служб Arduino IoT На следующей странице представлено множество информации, включая доступ к хранилищу документации (в основном для платформы Arduino) и доступ к IoT-сервисам. Чтобы начать использовать службы IoT, нажмите IoT Cloud в разделе Cloud apps (Облачные приложения), и вы попадете на домашнюю страницу сервисов, показанную на рис. 13.4. Рис. 13.4  Домашняя страница Arduino IoT Cloud
Начало работы с Arduino IoT Cloud  389 Показанная страница – ваша домашняя страница облака Arduino IoT. Если у вас есть созданные проекты (things), вы увидите их в списке, но при первом посещении вам нужно будет их создать. Однако начинать следует с создания устройства. Создать новое устройство Устройство в Arduino IoT Cloud – это наша плата MicroPython. Вам необходимо создать устройство, чтобы вы могли подключиться к облаку Arduino IoT, которое при первом подключении установит доверительное соединение с вашим устройством, используя идентификатор устройства и секретный ключ (подробнее об этом далее). Вы должны создать новое устройство для каждой платы, отправляющей данные. Хотя большинство функций специ­ фичны для Arduino, мы можем создать устройство, представляющее наши платы MicroPython. Чтобы создать новое устройство, щелкните вкладку Devices (Устройства) на информационной панели, затем нажмите Add (Добавить), как показано на рис. 13.5. Рис. 13.5  Создать новое устройство Затем вам необходимо выбрать семейство плат. Существует два варианта платформы Arduino, которые сгенерируют для вас пример кода (на языке Arduino C), но, к сожалению, у нас нет такой опции для MicroPython. Однако мы все равно можем создать устройство, выбрав опцию DIY Any Device (Любое устройство своими руками), как показано на рис. 13.6. Нажмите Continue (Продолжить) на следующем экране после прочтения ограничений использования этой опции, затем введите имя для своего устройства. Если вы следуете инструкциям здесь, назовите его RP2040 или как вам нравится. После изменения имени нажмите Next (Далее), как показано на рис. 13.7.
390  Arduino IoT Cloud Рис. 13.6  Выберите вариант DIY (Своими руками) Рис. 13.7  Назовите устройство
Начало работы с Arduino IoT Cloud  391 На следующем шаге вам будет предоставлен идентификатор вашего устройства и секретный ключ. Очень важно скопировать или куда-то загрузить эти данные, прежде чем двигаться дальше. Вы можете загрузить PDFфайл с ключами, нажав download the PDF (Загрузить PDF) и загрузив файл, который можно сохранить в безопасном месте для дальнейшего использования. Независимо от того, копируете ли вы ключи или загружаете файл .pdf, вы должны принять предупреждение о том, что идентификатор вашего устройства и секретный ключ не могут быть восстановлены. На рис. 13.8 показано диалоговое окно с замаскированными данными. Почему они скрыты? Потому что если вы знаете идентификатор устройства и секретный ключ, вы можете получить доступ к вещи, связанной с устройством, а это означает доступ к данным. Таким образом, вы не должны никому сообщать свой идентификатор устройства и секретный ключ. Рис. 13.8  Запишите идентификатор вашего устройства и секретный ключ
392  Arduino IoT Cloud ВНИМАНИЕ! Не забудьте записать идентификатор вашего устройства и секретный ключ! Вы не сможете их восстановить, и вам придется устройство создавать заново, ес­ ли вы их забудете. После сохранения данных нажмите Continue (Продолжить), а затем Got It (Понятно) в напоминании о документации (или щелкните ссылки, чтобы перейти к документации, но это только для платформы Arduino). Устройства связаны с вещами. Как только вы создадите свое устройство, вы увидите ссылку для создания новой вещи, как показано на рис. 13.9. Рис. 13.9  Создать вещь (thing) для устройства На этом общая настройка и настройка Arduino IoT Cloud завершены. Следующие шаги зависят от проекта (опять же, называемого здесь вещью). Мы создаем новую вещь (мысленно), как только узнаем, какие переменные хотим отправить в облако. Таким образом, вам следует создавать новую вещь, как только вы поймете, чем хотите заниматься. Давайте рассмотрим простой пример, чтобы завершить наш урок. Использование Arduino IoT Cloud с MicroPython В примере, который мы будем использовать для изучения использования Arduino IoT Cloud с MicroPython, будет включаться и выключаться встроенный светодиод платы MicroPython с помощью виртуального (облачного) выключателя. Таким образом, нам нужны две переменные: одна для того, чтобы светодиод сохранял свое состояние (включен или выключен), а другая для выключателя (опять же, включен или выключен). Аппаратным обеспечением в этом примере является просто плата контроллера. Как вы увидите, кода придется писать не так уж и много, но чтобы понять, как он работает, могут потребоваться некоторые усилия. Начинаем с создания новой вещи.
Начало работы с Arduino IoT Cloud  393 Создать новую вещь Когда все будет готово, нажмите CREATE THING (Создать вещь, см. рис. 13.9), чтобы начать создание. Вы увидите новый экран, который позволит вам добавлять переменные и менять название вещи, как показано на рис. 13.10. Рис. 13.10  Назовите вещь и добавьте переменные Чтобы изменить имя, щелкните имя (в настоящее время Untitled, т. е. без названия) и введите новое имя. Если вы хотите следовать этому примеру, назовите его testArduino. Чтобы добавить переменные, нажмите кнопку Add Variable (Добавить переменную). Мы создадим две логические переменные (типа boolean): ledSwitch для управления переключателем и onboardLed для встроенного светодиода. После того как вы нажмете кнопку добавления переменной, вы увидите диалоговое окно, которое позволит вам назвать переменную и выбрать ее тип, как показано на рис. 13.11 для переменной ledSwitch. Остальные настройки оставьте по умолчанию. После того как вы установили имя и выбрали тип boolean, нажмите Add Variable (Добавить переменную). Как только вы создадите переменную, вы увидите новое диалоговое окно для вашей вещи, в котором показаны созданные переменные. Чтобы добавить еще одну, нажмите кнопку Add (Добавить), дабы повторить процесс для переменной onboardLed. После добавления двух переменных вы должны увидеть переменные для вашей вещи, как показано на рис. 13.12. ПРИМЕЧАНИЕ Бесплатная учетная запись в Arduino IoT Cloud позволяет создавать два проекта (вещи). Вам необходимо перейти на платную учетную запись, если вам нужно подключать дополнительные проекты. Нажмите Upgrade (Обновление) в любой момент, чтобы изучить доступные функции для платных учетных записей, включая создание более двух вещей.
394  Arduino IoT Cloud Следующим шагом будет создание информационной панели для отображения элементов управления пользовательского интерфейса. Рис. 13.11  Создание переменной onSwitch Рис. 13.12  Вещь с переменными
Начало работы с Arduino IoT Cloud  395 Создание информационной панели Информационная панель – это то место, где мы будем строить наш интерфейс с помощью виджетов (элементов управления и отображения данных). Чтобы создать новую информационную панель, перейдите на вкладку Dashboards (Информационные панели), затем нажмите кнопку CREATE (Создать), как показано на рис. 13.13. Рис. 13.13  Создать новую информационную панель Когда вы создаете новую информационную панель, она представляет собой пустой лист, на который мы должны добавить виджеты. Чтобы изменить имя, щелкните текущее имя (Untitled) и измените его на SwitchLED, как показано на рис. 13.14. Чтобы добавить виджеты, нажмите кнопку Add (Добавить). Рис. 13.14  Новая информационная панель Когда вы нажмете кнопку добавления, то увидите длинный список виджетов, которые можно использовать. В нем вы найдете выключатель (switch), кнопку (pushbutton), ползунок (slider), светодиод (LED) и многие другие виджеты. Когда вы добавляете виджет на информационную панель, он связывается с переменной в проекте. Эта переменная затем предоставит данные для виджета. В случае с кнопкой мы хотим использовать логическую переменную типа boolean, где значения True и False будут контролировать, включен или выключен виджет. На рис. 13.15 показано начало списка доступных виджетов.
396  Arduino IoT Cloud Рис. 13.15  Добавление виджета на информационную панель В этом примере мы хотим добавить два виджета: выключатель и светодиод. Начнем с выключателя. Нажмите кнопку Add (Добавить) на информационной панели, а затем выберите выключатель (Switch) в списке. На рис. 13.16 показано диалоговое окно для настройки виджета, которое вы увидите. В данном случае мы хотим связать виджет с переменной switchLed. Рис. 13.16  Новый виджет (выключатель)
Начало работы с Arduino IoT Cloud  397 Обратите внимание, что вы можете присвоить виджету имя, если хотите (можно оставить его как Switch), и вы можете связать переменные, нажав кнопку с символом ссылки и надписью Link Variable (Связать переменную). Щелкните ссылку, чтобы открыть список переменных для вещей, и выберите переменную ledSwitch. Нам нужно выбрать нашу вещь (testArduino), затем переменную, потом нажать Link Variable, как показано на рис. 13.17. Рис. 13.17  Связать переменную с виджетом Завершив привязывание переменной, нажмите Done (Готово), чтобы добавить виджет на информационную панель. Повторите процесс, чтобы добавить виджет LED и связать его с переменной onboardLed. Примечание: чтобы найти виджет светодиода, вам, возможно, придется прокрутить список виджетов вниз. После добавления виджетов ваша информационную панель должна выглядеть так, как показано на рис. 13.18. Рис. 13.18  Готовая информационная панель (testArduino) Следующее, что нам нужно сделать, – это установить правильные библио­ теки для наших плат.
398  Arduino IoT Cloud Установка библиотек Arduino IoT Cloud Во-первых, вам необходимо установить mpremote (MicroPython Remote), программу удаленной установки MicroPython, которая упрощает установку биб­ лиотек на плату MicroPython. Команда pip3 install mpremote работает на любом ПК с установленным Python 3. Вам нужно запустить это только один раз на вашем компьютере. В лис­тин­ге 13.1 показаны результаты выполнения команды. Листинг 13.1  Установка mpremote C:\Users\Chuck> pip3 install mpremote Collecting mpremote Downloading mpremote-1.21.0-py3-none-any.whl (27 kB) Collecting importlib-metadata>=1.4 (from mpremote) Downloading importlib_metadata-6.8.0-py3-none-any.whl (22 kB) Collecting pyserial>=3.3 (from mpremote) Using cached pyserial-3.5-py2.py3-none-any.whl (90 kB) Collecting zipp>=0.5 (from importlib-metadata>=1.4->mpremote) Downloading zipp-3.17.0-py3-none-any.whl (7.4 kB) Installing collected packages: pyserial, zipp, importlib-metadata, mpremote Successfully installed importlib-metadata-6.8.0 mpremote-1.21.0 pyserial-3.5 zipp-3.17.0 Начните с закрытия всех приложений, подключенных к вашей плате, таких как Thonny. Затем введите следующую команду в командной строке, чтобы проверить COM-порт или путь к плате: C:\Users\Chuck> mpremote connect list COM6 5034C60632679C18 2341:025e Microsoft None Обратите внимание, что здесь отображается идентификатор устройства, который мы будем использовать вместо COM-порта (вам просто нужно указать идентификатор устройства с помощью параметра id). Далее нам нужно установить библиотеку Arduino IoT Cloud. В лис­тин­ге 13.2 показаны команда и пример вывода. При этом библиотека будет установлена на вашу плату, поэтому обязательно подключите плату к компьютеру. ПРИМЕЧАНИЕ На некоторых платформах вам может потребоваться запустить mpre- mote с помощью команды py -m mpremote. Листинг 13.2  Установка библиотек Arduino IoT Cloud C:\Users\Chuck> mpremote connect id:5034C60632679C18 mip install github:arduino/arduinoiot-cloud-py github:arduino/arduino-iot-cloud-py Install github:arduino/arduino-iot-cloud-py Installing github:arduino/arduino-iot-cloud-py/package.json to /lib Installing: /lib/arduino_iot_cloud/__init__.py Installing: /lib/arduino_iot_cloud/ucloud.py Installing: /lib/arduino_iot_cloud/umqtt.py Installing: /lib/arduino_iot_cloud/ussl.py
Начало работы с Arduino IoT Cloud  399 Installing senml (0.1.0) from https://micropython.org/pi/v2 to /lib Installing: /lib/cbor2/__init__.mpy Installing: /lib/cbor2/encoder.mpy Installing: /lib/cbor2/decoder.mpy Installing: /lib/senml/senml_pack.mpy Installing: /lib/senml/__init__.mpy Installing: /lib/senml/senml_record.mpy Installing: /lib/senml/senml_base.mpy Installing: /lib/senml/senml_unit.mpy Done Примечания по использованию Pico Pico хорошо работает в этой среде, но требует одной дополнительной биб­ лиотеки, поскольку образ MicroPython может ее не включать. Вам необходимо установить библиотеку ведения log-файла (logging library). Вы можете сделать это в Thonny, подключив свою плату, затем щелкнув Tools (Инструменты) ⇒ Manage packages… (Управление пакетами…), введя потом слово «logging» в поле поиска и нажав Search micropython-lib and PyPI (Поиск в micropython-lib и PyPI), как показано на рис. 13.19. Рис. 13.19  Добавление библиотеки (Thonny) Щелкните «logging library @ micropython-lib» и нажмите Install (Установить), чтобы завершить установку.
400  Arduino IoT Cloud Примечания по использованию Nano RP2040 Connect К сожалению, образ MicroPython для Nano RP2040 работает иначе, чем ожидалось от Pico. Код по-прежнему работает, но с библиотекой Arduino IoT Cloud для работы с Nano RP2040 требуется небольшая корректировка, поскольку образ MicroPython, который мы используем для Nano RP2040, не имеет биб­ лиотеки asyncio, но имеет библиотеку uasyncio. К счастью, мы можем открыть папку arduino_iot_cloud на плате, а затем отредактировать файл ucloud.py, как показано ниже жирным шрифтом. Это единственные строки кода, которые необходимо изменить. Внесите изменения, затем сохраните файл. import uasyncio as asyncio from uasyncio import CancelledError try: from uasyncio import InvalidStateError except (ImportError, AttributeError): # MicroPython doesn't have this exception class InvalidStateError(Exception): pass Теперь мы готовы начать писать код! Написание кода Код этого теста очень прост, и мы будем повторно использовать функцию connect() из предыдущих проектов. Для раздела импорта требуются только библиотеки network, sys, time и Pin, а также переменные (константы) из файла секретов. import network, sys, time from machine import Pin from project5.secrets import DEVICE_ID, SECRET_KEY, SSID, SSID_PASS Файл секретов (secrets.py) содержит показанные четыре значения. Обязательно обновите их, указав правильные значения идентификатора вашего устройства (DEVICE_ID) и секретного ключа (SECRET_KEY), а также SSID сети и пароля входа в сеть (SSID_PASS): # Arduino IoT Cloud Parameters DEVICE_ID = "DEVICEID" SECRET_KEY = "SECRETKEY" # WiFi values SSID = "SSIDNAME" SSID_PASS = "SSIDPASSWORD" Однако остальная часть кода требует небольшого пояснения. Мы управляем виджетами на панели управления, обновляя значение переменной. Мы делаем это с помощью экземпляра клиентского класса из библиотеки Arduino IoT Cloud, который передается в качестве параметра для функций
Начало работы с Arduino IoT Cloud  401 обратного вызова. Например, чтобы обновить переменную onboardLed для виджета LED, мы ссылаемся на переменную по имени в функции обратного вызова клиента следующим образом: client["onboardLed"] = value Изменения виджетов, например, при изменении состояния выключателя требуют предоставления функции обратного вызова, которая запускается этим событием. Нам понадобится один обратный вызов для выключателя, который не только устанавливает переменную для виджета светодиода, но и устанавливает значение для встроенного светодиода. Ниже показана функция обратного вызова on_switch(). Обратите внимание, что мы обновляем переменную светодиода onboardLed, связанную с виджетом LED. Таким образом, когда мы установим для этого параметра значение True, загорится светодиод на плате и включится светодиодный виджет. # Control the onboard led def on_switch(client, value): client["onboardLed"] = value Pin("LED").value(value) Обратите внимание, что параметрами функции являются экземпляр клиента и значение value. Значением является состояние переменной выключателя (switchLed), для которой установлено значение True или False в зависимости от того, в каком положении он находится. Последняя строка – это код, который устанавливает состояние встроенного светодиода на нашей плате (он работает и на Pico, и на Nano RP2040 Connect). Возможно, вы думаете, что на этом все закончилось? Нет, нам нужно сделать еще кое-что. Нам также необходимо инициализировать библиотеку Arduino IoT Cloud, а затем зарегистрировать каждый из виджетов. Этот код регистрации использует функцию register() для передачи имени переменной, ее начального значения, функции обратного вызова (при необходимости) и интервала проверки ее статуса. Здесь все становится неясным, поскольку документация по библиотеке MicroPython Arduino IoT Cloud несколько краткая. СОВЕТ Документацию по библиотеке можно найти в репозитории GitHub по адресу https://github.com/arduino/arduino-iot-cloud-py. Вы можете найти немного больше в папке примеров. Давайте рассмотрим код шаг за шагом. Во-первых, нам нужно инициализировать класс Arduino IoT Cloud следующим образом, передав идентификатор устройства device_id, имя пользователя (то же, что идентификатор устройства) и пароль, который является нашим секретным ключом от Arduino IoT Cloud: # Create a client object to connect to the Arduino IoT cloud client = ArduinoCloudClient(device_id=DEVICE_ID, username=DEVICE_ID, password=SECRET_KEY)
402  Arduino IoT Cloud Далее нам нужно зарегистрировать наши переменные. Нам нужны только минимальные параметры для onboardLed, поскольку это пассивный виджет (не имеет никаких действий). Однако для светодиодного переключателя нам нужно установить обратный вызов для функции on_write() и определить интервал 250 миллисекунд. Ниже приведены операторы регистрации для этих действий: # Register cloud objects client.register("onboardLed", value=False) client.register("ledSwitch", value=False, on_write=on_switch, interval=0.250) Наконец, мы просто запускаем клиента следующим образом. Как только мы это сделаем, у клиента заработает цикл, и наша единственная связь с управлением кодом – через нашу функцию обратного вызова. client.start() Это весь код, который нам нужен. Если у вас более сложные виджеты, регистрационный код может оказаться немного сложнее, но не торопитесь с документацией и изучите пример (в папке example/micrpython.py на GitHub по адресу https://github.com/arduino/arduino-iot-cloud-py) на предмет того, как они это делают. Вы сможете найти конфигурацию, подходящую для большинства основных виджетов. Если у вас есть более сложные виджеты, вы можете поискать вдохновение в документации библиотеки Arduino. Теперь давайте посмотрим на готовый код. В лис­тин­ге 13.3 показан код тестовой программы, хранящийся в файле test_arduino.py. Уделите несколько минут и просмотрите код, чтобы убедиться, что вы понимаете, как он работает. Листинг 13.3  Тестовый код для библиотеки Arduino IoT Cloud # # MicroPython for the IoT Second Edition - Chapter 13 # # Chapter 13 - Pedestrian Crossing with Arduino IoT Cloud Client # # Test the Arduino IoT Cloud Client # # Imports for the project import network, sys, time from machine import Pin from arduino_iot_cloud import ArduinoCloudClient from project5.secrets import DEVICE_ID, SECRET_KEY, SSID, SSID_PASS # Setup the board to connect to our network. def connect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(SSID, SSID_PASS) while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect...")
Начало работы с Arduino IoT Cloud  403 time.sleep(1) if not wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(wlan.ifconfig()[0])) return True # Control the onboard led def on_switch(client, value): client["onboardLed"] = value Pin("LED").value(value) # Main function def main(): # Connect to WiFi connect() # Create a client object to connect to the Arduino IoT cloud client = ArduinoCloudClient(device_id=DEVICE_ID, username=DEVICE_ID,password=SECRET_KEY) # Register cloud objects client.register("onboardLed", value=False) client.register("ledSwitch", value=False, on_write=on_switch, interval=0.250) print("Starting Arduino IoT Client.") # Start the Arduino IoT cloud client. client.start() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Выполнение кода Обязательно скопируйте файл secrets.py в папку на вашей плате MicroPython с именем project5. Это все, что нам нужно на данный момент. Когда все будет готово, просто запустите код test_arduino.py. Вы должны увидеть вывод, аналогичный следующему: Waiting to connect... Connected! My IP address is: 10.0.0.14 Starting Arduino IoT Client. Затем щелкните виджет переключателя на панели управления Arduino IoT Cloud и обратите внимание, что светодиод на панели управления и встро-
404  Arduino IoT Cloud енный светодиод загораются. Попробуйте это несколько раз. Возможно, вам придется установить выключатель в положение «выкл» перед запуском кода, если элемент управления не синхронизируется. СОВЕТ Чтобы синхронизировать информационную панель с кодом, вам может потребоваться установить переключатель в положение «выкл.» перед первым запуском. Убедившись, что все работает, сделайте вокруг своего стола победный круг! Вы только что написали проект интернета вещей, который управляет платой MicroPython из интернета. Ура! Теперь, когда мы ознакомились с началом работы в Arduino IoT Cloud, давайте посмотрим, как мы можем изменить наш проект из главы 9, чтобы использовать Arduino IoT Cloud в качестве интернет-приложения для пешеходных переходов. Проект: пешеходный переход IoT В этом проекте используется часть того же кода из главы 9, но с изменения­ ми. Кроме того, аппаратное обеспечение для этого проекта такое же, как в главе 9, но без аппаратной кнопки. Для удобства повторим информацию из главы 9. Необходимые компоненты В табл. 13.1 перечислены компоненты, которые вам понадобятся в дополнение к плате MicroPython и кабелю USB. Это то же самое, что приведено в главе 9, за исключением того, что здесь нет кнопки. Таблица 13.1. Необходимые компоненты Компонент Кол-во Ссылки Красный светодиод 2 www.sparkfun.com/products/9590 Желтый светодиод 1 www.sparkfun.com/products/9594 Зеленый светодиод 2 www.sparkfun.com/products/9592 Резисторы 220 или 330 Ом 5 www.adafruit.com/product/2780 Макетная плата (полноразмерная) 1 www.sparkfun.com/products/12615 www.adafruit.com/product/239 Перемычки «штырь/штырь» 11 www.adafruit.com/product/1950 Обязательно следуйте предупреждениям о номиналах резисторов, приведенным в главе 9. Теперь давайте посмотрим, как соединить компоненты вместе.
Проект: пешеходный переход IoT  405 Настройка оборудования Давайте посмотрим, как выполнить подключения для Raspberry Pi Pico и Arduino Nano RP2040 Connect. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 13.2 показаны соединения, необходимые для этого проекта. Таблица 13.2. Соединения для моделирования пешеходного перехода IoT (Pico)1 Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на макетной плате 38 GND Общий макетной платы (внизу) 17 GP13 Резистор для красного светодиода (светофор) 16 GP12 Резистор для желтого светодиода (светофор) 15 GP11 Резистор для зеленого светодиода (светофор) 11 GP8 Резистор для красного светодиода (пешеход) 10 GP7 Резистор для зеленого светодиода (пешеход) – Общий макетной платы (внизу) Все отрицательные выводы светодиодов – Резисторы Все положительные выводы светодиодов На рис. 13.20 показана схема подключения для проекта Pico. Рис. 13.20  Подключение проекта пешеходного перехода IoT (Pico) 1 На схеме и в таблице отсутствует вывод питания, так как к нему ничего не подключено, кроме платы контроллера, и предполагается, что плата питается от USB. – Прим. перев.
406  Arduino IoT Cloud Соединения для Arduino Nano RP2040 Connect В табл. 13.3 показаны соединения, необходимые для этого проекта. Таблица 13.3. Соединения для моделирования пешеходного перехода IoT (Nano RP2040) Обозначение на плате Nano RP2040 (номер GPIO / функция) Соединение 5V Питание макетной платы GND Общий макетной платы A5 (GP13) Резистор для красного светодиода (светофор) A4 (GP12) Резистор для желтого светодиода (светофор) A3 (GP29) Резистор для зеленого светодиода (светофор) A1 (GP27) Резистор для красного светодиода (пешеход) A0 (GP26) Резистор для зеленого светодиода (пешеход) Общий макетной платы Все отрицательные выводы светодиодов Резисторы Все положительные выводы светодиодов На рис. 13.21 показана схема подключения для проекта Nano RP2040. Рис. 13.21  Подключение проекта пешеходного перехода IoT (Nano RP2040) Еще раз: всегда проверяйте соединения перед включением платы! Следующий шаг – создать новую вещь и информационную панель в Arduino IoT Cloud (мы можем использовать то же устройство, но должны добавить дополнительные переменные). Настройка Arduino IoT Cloud Напомним, мы должны сначала создать переменные, а затем создать связанные с ними виджеты на новой информационной панели. Мы будем исполь-
Проект: пешеходный переход IoT  407 зовать то же устройство. Мы не будем повторять всех подробностей создания вещей и информационной панели, покажем только готовые конфигурации для каждой. Если вам нужны подробности, обратитесь к предыдущим разделам. Начнем с создания новой вещи. Создание новой вещи Войдите на arduino.cc и перейдите на панель управления IoT Cloud. Там создайте новую вещь с именем PedestrianCrossing с тремя логическими (boolean) переменными (button, walkGreen и walkRed). Мы будем использовать виджет-кнопку и два виджета светодиодов для представления красных и зеленых пешеходных светодиодов. Рисунок 13.22 показывает новую вещь с созданными переменными. Обратите внимание, что я удалил переменные, которые мы использовали в стартовом примере ранее, но вам не обязательно это делать. Рис. 13.22  Новая вещь (пешеходный переход) Далее нам нужно создать новую информационную панель. Создание новой информационной панели Создайте новую информационную панель и назовите ее PedestrianCrossing. Добавьте три виджета: кнопку, связанную с переменной button, светодиод, связанный с переменной walkGreen, и светодиод, связанный с переменной walkRed. Вы можете изменить цвет виджета светодиода на красный или зеленый, как показано на рис. 13.23. После создания виджетов вы можете щелкнуть значок пера на информационной панели и изменить порядок виджетов, перетаскивая их так, чтобы кнопка находилась слева, а светодиоды – справа, как показано на рис. 13.24.
408  Arduino IoT Cloud Рис. 13.23  Изменение цвета виджета светодиода Рис. 13.24  Новая информационная панель для пешеходного перехода Если вы решите отредактировать виджеты и щелкнете значок пера, вы можете изменить настройки виджетов, щелкнув вертикальное многоточие1, как показано на рис. 13.25. Значок появляется при наведении курсора мыши на виджет. 1 В Unicode этот знак называется Ellipsis. – Прим. перев.
Проект: пешеходный переход IoT  409 Рис. 13.25  Редактирование настроек виджета Это все, что касается пользовательского интерфейса! Теперь давайте поговорим о коде, который нам нужно написать. Написание кода Код этого проекта очень похож на код из главы 9, но с некоторыми изменениями в способе управления светодиодами. Мы должны использовать обратный вызов, чтобы инициировать цикл светофора, но мы не можем использовать функцию time.sleep(), как это было в главе 9. К счастью, мы можем использовать ReadTimer из главы 10, чтобы контролировать, как долго светодиоды остаются включенными. Мы импортируем обычные библиотеки из папки project5 вместе с нашим файлом секретов (secrets.py), который содержит следующие константы. Обязательно обновите их своими собственными данными. # Arduino IoT Cloud Parameters DEVICE_ID = "DEVICEID" SECRET_KEY = "SECRETKEY" # WiFi values SSID = "SSIDNAME" SSID_PASS = "SSIDPASSWORD" Напомним, наш код из главы 9 находился в одном модуле с именем pedestrian_crossing.py. В этом проекте мы перенесем весь код симуляции светофора в новый режим класса. Назовем новый модуль кода stoplight_iot.py и новый класс StoplightIoT. Давайте сначала посмотрим на код. Класс StoplightIoT Код из главы 9 – хорошая основа, но мы можем удалить оттуда весь код кнопки и переписать код для включения и выключения световых сигналов. Как вы увидите далее, большая часть кода аналогична тому, что мы использовали в главе 9, с добавлением класса ReadTimer вместо time.sleep(). Мы сохраняем возможность настраивать светодиоды для Pico или Nano RP2040 Connect.
410  Arduino IoT Cloud Ниже приведен импорт для нового класса. Нам нужны Pin, time и ReadTimer из папки project5: from machine import Pin import time from project5.read_timer import ReadTimer В конструкторе класса необходимо настроить светодиоды и таймер чтения. В лис­тин­ге 13.4 показан код конструктора. По сути, это тот же код, что и в главе 9. Однако мы используем переменную класса с именем self.walking в качестве переменной состояния, которая устанавливается в значение True при нажатии кнопки, сигнализируя о цикле сигнализации пешеходного перехода. Это станет яснее по мере изучения по коду. Листинг 13.4  Конструктор (класс StoplightIoT) def __init__(self, frequency=15000): # Pins for the Arduino Nano RP2040 Connect #stoplight_red = Pin(13, Pin.OUT) #stoplight_yellow = Pin(12, Pin.OUT) #stoplight_green = Pin(29, Pin.OUT) #pedestrian_red = Pin(27, Pin.OUT) #pedestrian_green = Pin(26, Pin.OUT) # Pins for the Pico stoplight_red = Pin(13, Pin.OUT) stoplight_yellow = Pin(12, Pin.OUT) stoplight_green = Pin(11, Pin.OUT) pedestrian_red = Pin(8, Pin.OUT) pedestrian_green = Pin(7, Pin.OUT) # Setup lists for the LEDs self.stoplight = [stoplight_red, stoplight_yellow, stoplight_green] self.pedestrian_signal = [pedestrian_red, pedestrian_green] # Turn off the LEDs for led in self.stoplight: led.off() for led in self.pedestrian_signal: led.off() # Start with green stoplight and red pedestrian_signal self.stoplight[2].on() self.pedestrian_signal[0].on() # Setup read event handler self.walk_event = ReadTimer(frequency) self.walking = False self.walk_event.reset() Далее нам нужна функция для переключения света. Эта функция ведет себя аналогично коду из главы 9, но вместо этого мы помещаем код в блок try
Проект: пешеходный переход IoT  411 на случай, если что-то пойдет не так. В лис­тин­ге 13.5 показан код функции. Обратите внимание, что мы проверяем ReadTimer и, если пешеход находится на переходе, соответствующим образом управляем светодиодами. Листинг 13.5  Функция переключения световых сигналов cycle_lights (класс StoplightIoT) # Function to check the status of the LEDs def cycle_lights(self, client): try: if self.walking and self.walk_event.time_to_read(): print("complete.") self.walking = False self.walk_event.reset() if not self.walking: # Stop=green, walk=red self.pedestrian_signal[1].off() self.pedestrian_signal[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it self.stoplight[0].off() self.stoplight[2].on() client["walkRed"] = True client["walkGreen"] = False self.walk_event.reset() if self.walking: # Blink the walk light self.pedestrian_signal[1].off() time.sleep_ms(500) self.pedestrian_signal[1].on() time.sleep_ms(500) except: pass Наконец, нам нужна функция, которая позволит реагировать на нажатие кнопки. Этот код является частью функции сигнального освещения, которая управляет светодиодами на плате и светодиодами на панели управления Arduino IoT Cloud. Код несложен, и мы оставим его объяснение читателю в качестве упражнения. В лис­тин­ге 13.6 показан код функции нажатия кнопки перехода. Листинг 13.6  Функция обратного вызова по нажатии кнопки # Callback function for when pushbutton is pressed on the dashboard def on_pushbutton(self, client, value): # Ignore any callbacks where button is not pressed # (value = True means button was pressed) if value and not self.walking: # Go yellow. self.stoplight[2].off() self.stoplight[1].on() # Wait 2 seconds
412  Arduino IoT Cloud time.sleep_ms(2000) # Go red and turn on walk light self.stoplight[1].off() self.stoplight[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it self.pedestrian_signal[0].off() self.pedestrian_signal[1].on() print("Walk initiated...", end="") self.walking = True self.walk_event.reset() client["walkRed"] = False client["walkGreen"] = True В лис­тин­ге 13.7 показан полный код класса StoplightIoT. Обязательно прочитайте его, пока не освоитесь и не поймете, как это работает. Не волнуйтесь, если это выглядит как много кода. Он не сильно отличается от кода в главе 9. Листинг 13.7  Новый класс StoplightIoT # # MicroPython for the IoT Second Edition - Chapter 13 # # Chapter 13 - Pedestrian Crossing with Arduino IoT Cloud # Client - Stoplight class # # Imports for the project from machine import Pin import time from project5.read_timer import ReadTimer class StoplightIoT: """Stoplight class for pedestrian crossing controlling LEDS and toggling LED widgets in Arduino IoT Cloud.""" # Constructor def __init__(self, frequency=15000): # Pins for the Arduino Nano RP2040 Connect #stoplight_red = Pin(13, Pin.OUT) #stoplight_yellow = Pin(12, Pin.OUT) #stoplight_green = Pin(29, Pin.OUT) #pedestrian_red = Pin(27, Pin.OUT) #pedestrian_green = Pin(26, Pin.OUT) # Pins for the Pico stoplight_red = Pin(13, Pin.OUT) stoplight_yellow = Pin(12, Pin.OUT) stoplight_green = Pin(11, Pin.OUT) pedestrian_red = Pin(8, Pin.OUT) pedestrian_green = Pin(7, Pin.OUT) # Setup lists for the LEDs self.stoplight = [stoplight_red, stoplight_yellow, stoplight_green] self.pedestrian_signal = [pedestrian_red, pedestrian_green]
Проект: пешеходный переход IoT  413 # Turn off the LEDs for led in self.stoplight: led.off() for led in self.pedestrian_signal: led.off() # Start with green stoplight and red pedestrian_signal self.stoplight[2].on() self.pedestrian_signal[0].on() # Setup read event handler self.walk_event = ReadTimer(frequency) self.walking = False self.walk_event.reset() # Function to check the status of the LEDs def cycle_lights(self, client): try: if self.walking and self.walk_event.time_to_read(): print("complete.") self.walking = False self.walk_event.reset() if not self.walking: # Stop=green, walk=red self.pedestrian_signal[1].off() self.pedestrian_signal[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it self.stoplight[0].off() self.stoplight[2].on() client["walkRed"] = True client["walkGreen"] = False self.walk_event.reset() if self.walking: # Blink the walk light self.pedestrian_signal[1].off() time.sleep_ms(500) self.pedestrian_signal[1].on() time.sleep_ms(500) except: pass # Callback function for when pushbutton is pressed on the dashboard def on_pushbutton(self, client, value): # Ignore any callbacks where button is not pressed # (value = True means button was pressed) if value and not self.walking: # Go yellow. self.stoplight[2].off() self.stoplight[1].on() # Wait 2 seconds time.sleep_ms(2000) # Go red and turn on walk light self.stoplight[1].off()
414  Arduino IoT Cloud self.stoplight[0].on() time.sleep_ms(500) # Give the pedestrian a chance to see it self.pedestrian_signal[0].off() self.pedestrian_signal[1].on() print("Walk initiated...", end="") self.walking = True self.walk_event.reset() client["walkRed"] = False client["walkGreen"] = True Теперь давайте посмотрим на основной код. Основной код Основной файл кода называется pedestrian_crossing_iot.py, и он содержит код, в котором нет ничего, чего мы до сих пор не видели. Нам нужно добавить функцию connect() из наших предыдущих проектов. Нам также необходимо настроить наш новый класс StoplightIoT. Код библиотеки Arduino IoT Cloud аналогичен нашему примеру ранее, но здесь мы будем использовать две функции обратного вызова: одну для кнопки, а другую для включения и выключения световых сигналов. Обе они предоставлены нам в классе StoplightIoT. Самая уникальная особенность кода – использование класса Task из биб­ лиотеки Arduino IoT Cloud. Класс Task позволяет нам настроить задачу, которая запускается периодически. В этом случае мы хотим, чтобы код проверял, была ли нажата кнопка, и если да, то запускал цикл смены светодиодов. Код для регистрации наших переменных и настройки задачи выглядит следующим образом: # Register cloud objects. client.register("walkRed", value=True) client.register("walkGreen", value=False) client.register("button", value=None, on_write=stoplight.on_pushbutton, interval=0.250) client.register(Task("user_task", on_run=stoplight.cycle_lights, interval=0.500)) В лис­тин­ге 13.8 показан полный код основного модуля. Листинг 13.8  Основной код (pedestrian_crossing_iot.py) # # MicroPython for the IoT Second Edition - Chapter 13 # # Chapter 13 - Pedestrian Crossing with Arduino IoT Cloud Client # # Imports for the project import network, sys, time from arduino_iot_cloud import ArduinoCloudClient, Task from project5.secrets import DEVICE_ID, SECRET_KEY, SSID, SSID_PASS
Проект: пешеходный переход IoT  415 from project5.stoplight_iot import StoplightIoT # Setup the board to connect to our network. def connect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(SSID, SSID_PASS) while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect...") time.sleep(1) if not wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(wlan.ifconfig()[0])) return True # Main function def main(): # Connect to WiFi connect() # Setup the walk timer stoplight = StoplightIoT(15000) # Create a client object to connect to the Arduino IoT cloud client = ArduinoCloudClient(device_id=DEVICE_ID, username=DEVICE_ID, password=SECRET_KEY) # Register cloud objects. client.register("walkRed", value=True) client.register("walkGreen", value=False) client.register("button", value=None, on_write=stoplight.on_pushbutton, interval=0.250) client.register(Task("user_task", on_run=stoplight.cycle_lights, interval=0.500)) print("Starting Arduino IoT Client.") # Start the Arduino IoT cloud client. client.start() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Уделите несколько минут и просмотрите код, пока не почувствуете, что понимаете, как он работает. Теперь давайте запустим этот проект!
416  Arduino IoT Cloud Выполнение кода Теперь мы готовы запустить код и протестировать его. Обязательно скопируйте все нужные файлы в папку project5. Сюда входят модули кода secret.py и stoplight_iot.py, а также модуль read_timer.py1 из главы 10. Запуск кода должен привести к тому, что красный светодиод на информационной панели загорится, а зеленый светодиод погаснет. Когда вы нажимаете кнопку (нажимаете и удерживаете более нескольких микросекунд), вы увидите, как на плате загорятся индикаторы, а на информационной панели загорится зеленый светодиод. Примерно через 30 секунд красный светодиод загорится, а зеленый светодиод погаснет. Вы обнаружите, что это работает хорошо, но, возможно, не с мгновенной реакцией, как в случае физической кнопки. Вы можете улучшить время реакции, отрегулировав интервал выполнения задач, но оно и так работает приемлемо. Развлекитесь с этим какое-то время. А что дальше? Этот проект показывает отличные перспективы для повторного использования методик в других проектах. Это особенно верно, поскольку теперь мы научились использовать аналоговые устройства (светодиоды). Теперь вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из тех, которые вы можете рассмотреть. Какие-то из них просты, другие могут оказаться сложной задачей или потребовать дополнительных исследований и более сложного кодирования:   используйте NeoPixels (www.adafruit.com/category/168) вместо свето­ диодов. Это RGB-светодиоды, поэтому вам понадобится всего два – один для светофора и один для пешеходного перехода. См. https:// github.com/JanBednarik/micropython-ws2812 для получения дополнительной информации и примеров;   добавьте счетчик, отслеживающий количество запросов на пересечение, и отобразите его на информационной панели;   используйте OLED из проекта главы 8 вместо светодиодов для указателя пешехода, чтобы показывать «Идти» или «Стоять». Добавьте виджет на панель управления для отображения этой информации;   добавьте еще один светофор, чтобы завершить симуляцию пешеходного перехода;   добавьте еще три светофора и расширьте симуляцию, включив в нее управление светофорами в двух направлениях;   как только у вас заработают четыре светофора, добавьте второй пешеходный переход для другого перекрестка и новую кнопку на ин1 См. сноску на стр. 348. – Прим. перев.
Итоги  417 формационной панели. Совет: программирование времени для этого решения может быть немного трудоемким, поэтому не торопитесь и прорабатывайте его по частям. Потратьте некоторое время на изучение этих потенциальных улучшений – это будет хорошей практикой. Итоги Работа с дискретными электронными компонентами и управление ими через интернет – увлекательное занятие и новый захватывающий навык. Благодаря облачным сервисам, таким как Arduino IoT Cloud, удаленное управление IoT-проектами становится проще. В этой главе мы реализовали моделирование пешеходного перехода с помощью удаленной кнопки, управляемой через интернет. Мы использовали серию светодиодов для обозначения светофора и сигнала пешехода, также управляемого через интернет. В следующей главе мы рассмотрим еще один IoT-проект с поддержкой облачных сервисов, использующий датчики погоды и облачные сервисы Adafruit IO Cloud.
Глава 14 MQTT с Adafruit IO Для создания полноценного проекта интернета вещей необходимо использовать технологии, позволяющие плате MicroPython собирать и отправлять данные в службы в интернете, которые могут хранить, извлекать и визуализировать данные. Такие интернет-сервисы обычно представляют собой облачные сервисы. Скорее всего, вы использовали некоторые из этих технологий, не зная об этом. Мы уже видели раннюю, простую форму подобных операций в главах 10 и 11 – использование HTML-сервера для отправки данных клиенту через интернет. Отправка данных через HTML может подойти для некоторых проектов, но проекты, в которых вам может потребоваться представить данные в какойлибо другой форме или выполнить анализ данных, потребуют более продвинутого механизма для передачи и хранения данных. Существует множество таких технологий, в том числе позволяющих контролировать передачу данных. Подобные технологии имеют определенный протокол (способ взаимодействия) и часто поддерживаются специализированными программными интерфейсами. Как мы уже говорили (см. раздел «Протокол сообщений телеметрии (MQTT)» в главе 12), один из самых простых в использовании с микроконтроллерами способов определяет протокол с ролями публикации и подписки. То есть вы можете публиковать данные (записывать) или подписываться на данные (читать) и даже получать уведомления о появлении новых данных. Один из подобных простых протоколов публикации/подписки называется транспортом телеметрии очереди сообщений (MQTT). В этой главе мы будем использовать MQTT, чтобы увидеть пример полного проекта интернета вещей, который считывает данные с датчиков и отправляет данные на сервер, к которому затем могут получить доступ клиенты, использующие MQTT для подписки на данные. Самое приятное то, что некоторые службы MQTT, подобные тому, который мы будем использовать в этой главе, также предоставляют инструменты визуализации, которые позволяют вам видеть данные по мере их создания. Мы будем использовать то же оборудование и большую часть того же кода, что и в главе 11. Изменения в этом проекте связаны с использованием об-
Обзор  419 лачных сервисов для размещения данных и MQTT для публикации данных. Как вы увидите, построить его не так уж и сложно. Начнем с обзора MQTT и Adafruit IO. Обзор Adafruit IO (io.adafruit.com) – это облачная система визуализации данных, которая проста в использовании и требует очень мало знаний программирования. Это достигается за счет поддержки технологии REST1 и API MQTT. Для этого проекта мы будем использовать MQTT API. Целью Adafruit IO было устранить всю сложность существующих систем регистрации данных и облачных сервисов обработки данных и сделать их простыми в использовании, и Adafruit справилась с этой задачей очень хорошо. Вкратце, мы используем клиентский драйвер (библиотеку) MQTT, пишем код для подключения и подписываемся на данные или публикуем данные. Часть визуализации происходит на самом сервере Adafruit IO, где мы создаем собственный пользовательский интерфейс для просмотра данных. Чтобы начать работу с Adafruit IO, необходимо выполнить четыре шага: 1) 2) 3) 4) создать учетную запись; настроить каналы (очереди сообщений) для ваших данных; настроить панель мониторинга для визуализации данных; подключить свои устройства и начать публиковать и подписываться. Все, что вам нужно для начала работы с Adafruit IO, – это учетная запись пользователя. Чтобы создать учетную запись для Adafruit IO, просто зайдите на https://io.adafruit.com/. Если у вас уже есть учетная запись на сервере Adafruit, вы можете использовать ее и просто зарегистрироваться для доступа к Adafruit IO. Процесс прост и легок в исполнении. После входа в систему вы увидите административный интерфейс, в котором сможете создавать каналы (feeds) и информационные панели. Канал – это основной компонент Adafruit IO. Канал – это место, где вы размещаете свои данные в виде сообщений со своих устройств. Каналы могут хранить данные (через публикацию), а устройства (клиенты) могут читать данные, подписавшись на канал. У вас может быть несколько каналов, каждый из которых определяется своим именем и на него ссылается ваш идентификатор пользователя. Информационные панели – это представление данных в каналах. Adafruit IO обеспечивает интерфейс перетаскивания (drop-интерфейс) для очень быстрого создания простых представлений данных с использованием предопределенных элементов управления пользовательского интерфейса, называемых «блоками». Каждая информационная панель может иметь один 1 См. раздел «Сервис ThingSpeak» в главе 12. – Прим. перев.
420  MQTT с Adafruit IO или несколько блоков, которые можно подключить к каналам. Данные отображаются в блоках, автоматически обновляемых при поступлении новых данных. После настройки каналов и информационной панели мы можем написать код для использования библиотеки MQTT и отправки или получения данных. Затем мы можем вернуться на нашу информационную панель и просмотреть данные. Подробное описание настройки каналов и информационной панели проекта мы увидим в следующем разделе. Если вы хотите узнать больше об Adafruit IO, ознакомьтесь с этими уроками от Adafruit:   Adafruit IO: https://learn.adafruit.com/adafruit-io/rest-api?view=all;   основы Adafruit IO – каналы: https://learn.adafruit.com/adafruit-io-basicsfeeds/resources?view=all;   основы Adafruit IO – информационные панели: https://learn.adafruit. com/adafruit-io-basics-dashboards/creating-a-dashboard?view=all;   MQTT и Adafruit IO: https://learn.adafruit.com/mqtt-adafruit-io-and-you/ arduino-plus-library-setup?view=all. Вы также можете найти некоторые интересные идеи проектов по использованию Adafruit IO по адресу https://learn.adafruit.com/search?q=io.adafruit. com&. Базовая служба MQTT состоит из трех компонентов: сенсорные узлы (пуб­ ликаторы), которые создают сообщения, содержащие данные, клиенты (подписчики), которые читают сообщения, и брокер (сервер), который хранит сообщения и распределяет их среди подписчиков. На рис. 14.1 показано представление о том, как работают эти три компонента. MQTT Broker Key Подписчик Публикатор Сенсорные узлы Визуализация данных Рис. 14.1  Концепция MQTT MQTT client Клиент MQTT
Обзор  421 Обратите внимание, что слева у нас есть сенсорные узлы, которые могут публиковать данные (зеленые сплошные линии), справа – клиенты, которые могут подписываться на сообщения (сиреневые пунктирные линии), а также компонент визуализации, предоставляемый брокером (красные пунктирные линии). Обратите внимание также, что существуют сенсорные узлы, которые могут публиковать и подписываться, а также клиент, который может визуализировать данные и подписываться на сообщения. Фактически вы можете использовать любую комбинацию публикации и подписки. Мы увидим, как публиковать и подписываться на сообщения, из нашего проекта MicroPython. Клиенты Клиент MQTT – это просто устройство (или компьютер), имеющее библиотеку MQTT, которую вы можете использовать для программирования вашего устройства на предмет отправки сообщений (публикации) или чтения сообщений (подписки) от брокера. MicroPython включает библиотеку MQTT. Брокеры Вы можете использовать несколько брокеров, в том числе предоставляемые облачными сервисами, а также те, которые вы можете установить на своем собственном сервере. Большинство брокеров имеют собственную реализацию клиента MQTT (некоторым требуются определенные драйверы или библиотеки) и могут работать на определенной платформе либо требовать других компонентов. Несмотря на это, они поддерживают один и тот же протокол MQTT от сенсорных узлов и клиентов. Таким образом, вы можете использовать один брокер MQTT, а затем перейти к другому, не переписывая код с нуля. Для проектов интернета вещей предпочтительно выбрать облачного брокера, чтобы вы могли подключить свою плату к облаку и получить доступ к данным из любого места. Брокером для данного проекта является Adafruit IO. Существуют некоторые ограничения, ни одно из которых не повлияет на эксперименты, подобные проекту в этой главе. Если вы планируете создать коммерческий продукт на базе Adafruit IO, возможно, вам стоит рассмотреть вариант платного брокерского обслуживания. Альтернативные MQTT-брокеры Сайт https://github.com/mqtt/mqtt.github.io/wiki/servers содержит список MQTT-бро­ керов. Вы найдете те, которые подходят для конкретной платформы или языка программирования, например Java. Вы также найдете некоторые коммерческие сервисы MQTT, которые можно использовать для более крупных или коммерческих проектов.
422  MQTT с Adafruit IO Однако если вы хотите настроить собственный MQTT-сервер в своей сети, вы можете использовать Mosquito (http://mosquitto.org/), который имеет открытый исходный код и очень популярен среди любителей. Вы даже можете использовать общедоступный тестовый сервер. Более того, вам не придется сильно менять код в этом проекте, чтобы его использовать. Mosquito следует рассматривать как ступеньку от бесплатного MQTT-брокера, такого как Adafruit IO, к коммерческому MQTT-брокеру. Начало работы с Adafruit IO Теперь давайте создадим простой проект для изучения использования MQTT с Adafruit IO. Вместо того чтобы создавать новый проект с новыми компонентами, мы будем использовать тестовый проект, который повторяет погодный датчик из главы 11. Данные здесь не в центре внимания; скорее, основное внимание уделяется тому, как начать работу с MQTT и как подключить наш код к сервису Arduino IO. Первое, что нам нужно сделать, – это создать учетную запись. Создать учетную запись на Adafruit IO Начните с посещения https://accounts.adafruit.com/users/sign_in. Если у вас уже есть учетная запись на сайте www.adafruit.com, вы можете использовать ее для входа в Adafruit IO. В противном случае вам необходимо создать учетную запись, нажав SIGN UP (Регистрация) и следуя инструкциям для создания учетной записи. Получив учетную запись, вы можете щелкнуть ссылку IO в верхней части страницы, как показано на рис. 14.2. Рис. 14.2  Доступ к Adafruit IO через вход в Adafruit Далее нам нужно настроить каналы ввода-вывода и информационную панель, а затем написать код, прежде чем мы будем готовы протестировать проект.
Начало работы с Adafruit IO  423 Настройка Adafruit IO Теперь мы готовы настроить Adafruit IO. Для нашего примера проекта мы создадим только один канал. На информационной панели будет несколько блоков для отображения каждого из каналов. Давайте посмотрим, как создать каналы и информационную панель для нашего проекта. Настройка каналов Чтобы настроить канал, войдите в Adafruit IO и выберите Feeds (Каналы) из списка ссылок в левой части экрана, затем нажмите + New Feed (Новый канал), как показано на рис. 14.3. Рис. 14.3  Создание нового канала Далее вы можете указать имя и описание (необязательно) канала. Назовите первый канал «Temperature» (Температура), как показано на рис. 14.4. Когда вы будете удовлетворены введенными данными, нажмите Create (Создать), чтобы создать канал. Рис. 14.4  Диалоговое окно Create a New Feed Повторите процесс и создайте каналы для хранения данных о влажности с именем «Humidity» (Влажность) и барометрическом давлении с именем
424  MQTT с Adafruit IO «Pressure» (Давление). По завершении вы должны увидеть новые каналы, как показано на рис. 14.5. Рис. 14.5  Каналы для проекта погоды Обратите внимание, что на данный момент в каналах нет данных. Мы не увидим никаких данных, пока не настроим и не подключим нашу плату MicroPython. Теперь, когда каналы настроены, мы можем создать информационную панель, чтобы видеть данные после их публикации. Настройка информационной панели Вы можете создать столько информационных панелей, сколько хотите, чтобы просматривать данные в своих каналах. Фактически вы можете создать одну панель для каждого канала или несколько панелей, которые подключаются к одному или нескольким вашим каналам. Для этого проекта мы создадим одну информационную панель, которая будет подключена ко всем четырем каналам. Чтобы настроить информационную панель, выберите Dashboards (Информационные панели) из списка ссылок в левой части экрана, затем нажмите + Dashboard, как показано на рис. 14.6. Рис. 14.6  Создание новой панели Далее вы можете указать имя и описание (необязательно) для созданной панели. Назовите панель «WeatherData» и укажите описание (необязательно), как показано на рис. 14.7. Когда вы будете удовлетворены введенными данными, нажмите Create (Создать), чтобы создать панель мониторинга. После того как вы создали информационную панель, вам необходимо отредактировать ее, чтобы добавить блоки. Щелкните информационную па-
Начало работы с Adafruit IO  425 нель, чтобы открыть ее для редактирования. Доступные блоки и их использование показаны в табл. 14.1. Некоторые блоки могут быть подключены к одному и только одному каналу, а другие могут быть подключены к нескольким каналам. Рис. 14.7  Диалоговое окно создания информационной панели Таблица 14.1. Блоки Adafruit IO Имя Тип Описание Toggle button (Кнопкапереключатель) Вход Выбор одного из двух значений, текстовых или числовых. Представляйте себе это как переключатель Momentary button (Нажимная кнопка) Вход Отправка одного значения в канал, аналогично аппаратной тактовой кнопке Number slider Вход (Числовой ползунок) Выбор числа из указанного диапазона, который вы задаете Gauge (Измеритель) Выход Отображать текущую величину канала. Может показывать процент, если вы установите минимальное и максимальное значения Text box (Текстовое поле) Оба Stream (Поток) Выход Отображать сообщения из одного или нескольких каналов Image (Картинка) Выход Показывать изображения в канале Line graph (Линейный график) Выход Отображение данных из ленты в виде линейного графика Color picker (Выбор цвета) Вход Map (Карта) Выход Отслеживание местоположения данных вашего канала (если географические данные доступны) Remote control (Пульт управления) Вход Отображать статический текст или текст из канала Выбор значения RGB и отправка его в канал Имитация мини-пульта дистанционного управления
426  MQTT с Adafruit IO Для нашего проекта мы добавим следующие блоки:   Line graph (Линейный график): по одному такому блоку для подключения каналов температуры, влажности и давления;   Stream (Поток): один блок для просмотра необработанных данных с платы. Чтобы добавить блок, щелкните раскрывающийся список со значком шес­ теренки в правом верхнем углу экрана редактирования информационной панели, как показано на рис. 14.8. Рис. 14.8  Добавить блок Затем нажмите в меню Create New Block (Создать новый блок), как показано на рис. 14.9. Рис. 14.9  Настройки информационной панели Когда вы нажмете Create New Block, вам будет показан список доступных блоков. Блоки, перечисленные в табл. 14.1, показаны на рис. 14.10 и перечис-
Начало работы с Adafruit IO  427 лены в порядке, начиная с верхнего левого угла. Щелкните блок по вашему выбору, чтобы добавить его на информационную панель. Рис. 14.10  Доступные блоки При выборе блока необходимо выбрать каналы для блока. На рис. 14.11 показан пример выбора канала температуры для линейного графика. Как видите, вы можете выбрать любое количество каналов. Однако обязатель-
428  MQTT с Adafruit IO но учитывайте масштаб каждого из данных, прежде чем использовать линейный график для нескольких каналов. В этом проекте мы имеем разные масштабы для трех типов данных. Если бы мы использовали однолинейный график, канал с наименьшим диапазоном отображался бы внизу графика и мог бы скрывать изменение данных с течением времени (он мог бы выглядеть как плоская линия). Рис. 14.11  Выбор каналов для блока После того как вы выбрали каналы, установив флажок рядом с названием канала, нажмите Next step (Следующий шаг), чтобы настроить блок. Диалог будет выглядеть по-разному для каждого типа блока. На рис. 14.12 показаны настройки блока линейного графика температуры. Обратите внимание, что я решил установить для блока то же имя, что и для канала, и изменил метки для осей X и Y. Я также установил минимальное и максимальное значения для оси Y. Когда вы будете удовлетворены настройками, нажмите Create block (Создать блок), чтобы добавить блок на панель управления. Если вы хотите видеть только те данные, которые записываются во время просмотра информационной панели, выберите Live (В реальном времени) для параметра Show History (Показать историю) или можете выбрать диапазон, например один час, чтобы отображать данные за последний час. Потратьте некоторое время, чтобы обдумать, что вы хотите увидеть, а затем соответствующим образом измените этот параметр, как указано в разделе «Выполнение кода» далее. Продолжайте и создайте еще два линейных графика для потоков влажности и давления. Вы можете назвать блоки аналогичным образом, но использовать диапазон 0–100 для влажности и диапазон 800–1200 для давления.
Начало работы с Adafruit IO  429 Возможно, вам захочется изменить диапазоны, если высота вашего географического местоположения отличается, что приводит к немного другим величинам. Рис. 14.12  Настройки блока (температура) Наконец, добавляем блок для протоколирования всех данных. Для этого мы будем использовать блок потока Stream. Добавьте блок и выберите все три канала. Вы можете назвать его как хотите, и подойдут настройки по умол-
430  MQTT с Adafruit IO чанию. На рис. 14.13 показаны использованные мной настройки. Этот блок удобно использовать при настройке новой информационной панели, поскольку он позволяет видеть данные по мере их поступления в каждое поле. Рис. 14.13  Настройки блока потока Stream Необходимо выполнить еще один шаг. При добавлении блоков на панель управления они размещаются на экране в том порядке, в котором они появляются, и с размерами по умолчанию. Вы можете перемещать блоки, щелкнув значок шестеренки и выбрав Edit Layout (Редактировать расположение), как показано на рис. 14.14. Вы также можете изменить размер блоков, чтобы они соответствовали вашему макету. Вы можете в результате получить панель, подобную показанной на рис. 14.15. Завершив редактирование информационной панели, нажмите Save Layout (Сохранить расположение). Вы всегда можете отредактировать свою информационную панель позже, если захотите изменить масштаб одного из
Начало работы с Adafruit IO  431 блоков или переместить блоки. Чтобы отредактировать уже добавленный блок, щелкните маленький значок шестеренки на экране редактирования информационной панели для выбранного блока. Рис. 14.14  Редактировать расположение на информационной панели Рис. 14.15  Выстроенные блоки на информационной панели
432  MQTT с Adafruit IO Получите свои учетные данные Есть еще одна вещь, которую вам нужно сделать. Подключение к Adafruit IO осу­ ществляется через вашу учетную запись пользователя и специальный ключ, сгенерированный системой. Эти данные используются вместо идентификатора Рис. 14.16  Кнопка получения ключа пользователя и пароля. Чтобы получить ключ, нажмите желтую кнопку ключа, как показано на рис. 14.16. Вы увидите диалоговое окно, подобное показанному на рис. 14.16 (данные замаскированы для обеспечения безопасности). С этими данными следует обращаться так же, как с любым другим паролем. На рис. 14.17 показан пример ключей Adafruit 10. Обратите внимание, что есть примеры использования ключей в коде далее. Рис. 14.17  Ключи Adafruit IO При написании кода вам понадобится и ваше имя пользователя, и ваш ключ, поэтому лучше получить эти значения сейчас и сохранить их в безопас­
Проект: погода IoT  433 ном месте. Вы всегда можете обратиться к этой странице в будущем, если не хотите сохранять эти значения в файле. Кроме того, если вам когда-нибудь понадобится восстановить ключ, вы можете сделать это, нажав REGENERATE KEY (Восстановить ключ), но не делайте этого, если вам не нужен новый ключ! Хорошо, теперь мы готовы начать отправку данных. Давайте посмотрим, как это сделать. Проект: погода IoT В этом проекте используется тот же код из главы 11, но с некоторыми существенными изменениями. Аппаратное обеспечение для данного проекта аналогично главе 11. Таким образом, мы составим полный код проекта, но опустим обсуждение фрагментов кода и подробности аппаратного обеспечения, которые привели в главе 11. Теперь посмотрим, какие компоненты нужны для этого проекта, а затем посмотрим, как все соединить воедино. Повторяем аппаратные подключения для удобства. Необходимые компоненты В табл. 14.2 перечислены компоненты, которые вам потребуются. Как видите, нам нужен только датчик BME280. Мы не будем использовать RTC, поскольку Adafruit IO будет записывать дату и время каждого образца, загруженного в канал. Таблица 14.2. Необходимые компоненты Компонент Кол-во Описание Ссылки BME280 1 Weather sensor www.sparkfun.com/products/13676 www.adafruit.com/product/2652 Перемычки 4 Перемычки «штырь/штырь», www.sparkfun.com/products/9838 6 или 7 дюймов www.adafruit.com/product/1950 Схема этого проекта проще, чем в двух предыдущих проектах. Теперь давайте посмотрим, как соединить компоненты вместе. Настройка оборудования Настройка оборудования для этого проекта проще из-за того, что у нас есть только датчик BME280.
434  MQTT с Adafruit IO Мы его подключим к Raspberry Pi Pico и Arduino Nano RP2040 Connect. Соединения аналогичны, но места подключения проводов к платам различаются. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 14.3 показаны подключения, необходимые в этом проекте для Raspberry Pi Pico. Таблица 14.3. Соединения для проекта мониторинга погоды (Pico) Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на внешней плате 36 3V3 BME280 – 3.3V 38 GND BME280 – GND 11 GP8 BME280 – SDA 12 GP9 BME280 – SCL На рис. 14.18 показана схема подключения для проекта Pico. Рис. 14.18  Подключение IoT-проекта мониторинга погоды (Pico) Соединения для Arduino Nano RP2040 Connect Поскольку мы используем те же компоненты для Arduino Nano RP2040 Connect, соединения будут аналогичными, но, как отмечалось выше, Nano RP2040 имеет другое расположение контактов. Таким образом, некоторые связи будут отличаться. В табл. 14.4 показаны соединения, необходимые для Nano RP2040.
Проект: погода IoT  435 Таблица 14.4. Соединения для проекта мониторинга погоды (Nano RP2040) Обозначение на плате Nano RP2040 (номер GPIO / функция) Вывод на внешней плате 3.3V BME280 – 3.3V GND BME280 – GND A4 (GP12. SDA) BME280 – SDA A5 (GP13, SCL) BME280 – SCL На рис. 14.19 показана схема подключения для проекта Nano RP2040. Рис. 14.19  Подключение IoT-проекта мониторинга погоды (Nano RP2040) Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте плату – необходимо немало обсуждений, прежде чем мы будем готовы протестировать проект. Написание кода Код для этого проекта относительно легко написать, если разобраться в биб­ лиотеке MQTT. Как вы увидите, он хорошо сочетается с Adafruit IO по простоте использования. Чтобы сделать этот проект более интересным и применимым к тому, как вы будете писать проекты IoT для платформы MicroPython, мы разместим весь код для чтения датчиков и публикации данных в классе. Затем будем использовать этот класс в модуле кода, который мы сможем вызвать из нашего модуля кода weather_iot.py при загрузке. Поскольку в коде не используются какие-либо конструкции, которые мы еще не видели, описание
436  MQTT с Adafruit IO кода будет кратким и сосредоточено только на новых концепциях и конкретно на коде MQTT. Однако сначала нам нужно загрузить библиотеку MQTT и из главы 11 драйвер BME280. Установка библиотеки MQTT для MicroPython Как и в предыдущей главе, нам нужно установить библиотеку Arduino IoT Cloud с помощью программы mpremote. Если вы не установили mpremote, см. главу 13 о том, как установить ее на ваш компьютер. Далее нам нужно определить, куда подключена плата MicroPython. Прежде чем запускать следующую команду, выделенную жирным шрифтом, убедитесь, что плата подключена к вашему ПК: C:\Users\Chuck> mpremote connect list COM15 E661AC8863913F23 2e8a:0005 Microsoft None ПРИМЕЧАНИЕ Если плата подключена с помощью редактора кода, такого как Thonny, вам необходимо закрыть приложение перед запуском этой команды. В выходной строке показан порт, к которому подключена плата. Это понадобится нам на следующем шаге для установки библиотеки umqtt.simple. Результат команды mpremote connect COM15 mip install umqtt.simple показан ниже. Обязательно используйте правильный COM-порт (Windows) или путь (Mac и Linux). C:\Users\Chuck> mpremote connect COM15 mip install umqtt.simple Install umqtt.simple Installing umqtt.simple (latest) from https://micropython.org/pi/v2 to /lib Installing: /lib/umqtt/simple.mpy Done Как только эта команда завершится и вы снова откроете Thonny, вы увидите новую папку на вашей плате MicroPython с именем lib, которая содержит установленные вами библиотеки. На рис. 14.20 показан пример того, как дерево файлов будет отображаться в Thonny. Рис. 14.20  Установленные библиотеки (mpremote) После установки библиотеки MQTT мы готовы написать код датчика и основной код. Но сначала давайте напомним применяемый здесь метод напи-
Проект: погода IoT  437 сания программ, содержащих конфиденциальные данные, такие как пароли и ключи ввода-вывода. Секретный файл Обычной практикой для проектов, содержащих конфиденциальные переменные или константы, является включение файла секретов, хранящегося в файловой структуре Python. Например, общепринятой практикой является сохранение файла с именем secrets.py, куда мы помещаем наш идентификатор пользователя и пароль Wi-Fi вместе с любыми другими данными, которые мы хотим сохранить. Ниже показан пример содержимого файла secrets. py, используемого для этого проекта. Создайте его со своими данными и сохраните на компьютере. Позже мы скопируем его на нашу плату MicroPython. SSID = "yadayada" SSID_PASS = "yadayadayada" IO_USER ="yada" IO_KEY = "yadayadayadayadayadayadayadayadayada" Мы включаем эти переменные (константы) с помощью оператора импорта, например такого: from project6.secrets import SSID, SSID_PASS, IO_USER, IO_KEY Далее нам нужно создать класс для чтения данных с датчика. Класс датчика погоды Подобный класс, который считывает данные с датчика BME280, мы написали в главе 11. Там мы узнали, что можем упаковать наш код таким образом, чтобы упростить использование набора операций с данными. Мы сделаем то же самое здесь. Мы обернем наш код вокруг датчика BME280 и Adafruit IO, чтобы создать простой в использовании класс или библиотеку для чтения данных о погоде и публикации их в облаке. Мы назовем класс WeatherSensorIoT, поскольку он включает чтение датчика и публикацию данных. Модуль кода назовем weather_sensor_iot.py. Вместо того чтобы вдаваться в каждую деталь, сосредоточимся на основных моментах кода. Начнем с обзора раздела импорта. Для раздела импорта требуется несколько библиотек. Нам понадобятся биб­лиотеки I2C и MQTT, драйвер BME280 и библиотека времени для задержек. Ниже показан импорт, необходимый для класса: import time from umqtt.simple import MQTTClient from project6.bme280 import BME280 Конструктору необходимо принять имя пользователя, зарегистрированного в Adafruit IO, ключ IO и частоту обновления показаний. Идентификатором устройства может быть любая строка, которая вам нравится, но рекомендует-
438  MQTT с Adafruit IO ся использовать для устройства короткое имя. Мы также будем использовать параметр для порта сервера MQTT на случай, если вы захотите его изменить, но здесь мы будем использовать значение по умолчанию 1883, которое является портом для Adafruit IO. В лис­тин­ге 14.1 показан конструктор класса. Обратите внимание, что здесь также настраивается датчик BME280. Листинг 14.1  Конструктор (класс WeatherSensorIoT) # Constructor def __init__(self, i2c, io_user, io_key, frequency, port=1883): # Save variables passed for use in other methods self.io_user = io_user self.io_key = io_key self.update_frequency = frequency self.port = port # Now, setup the sensor self.sensor = BME280(i2c=i2c, address=0x77) time.sleep(0.100) print("Weather MQTT client is ready.") Обратите внимание, что эта функция просто инициализирует несколько переменных класса (обозначаемых self.) для хранения значений датчика и частоты обновления. Конструктор также инициализирует интерфейс I2C и экземпляр класса датчика BME280. Функция чтения данных такая же, как мы использовали в главе 11, но без записи в log-файлы. Следующая функция, которая нам понадобится, – это функция, которую мы можем использовать в качестве обратного вызова для сообщений MQTT из каналов. Мы назовем функцию message_callback(). В данном случае нам нужны только параметры темы и сообщения. Ниже показан код функции message_callback(): # A simple callback to print the message from the server def message_callback(self, topic, msg): print("[{0}]: {1}".format(topic, msg)) Последняя функция, которая нам нужна, – это та, которая будет выполнять всю работу по сбору данных с датчика и публикации их в наших лентах на Adafruit IO. Здесь и кроется суть использования библиотеки MQTT, поэтому мы немного задержимся на этом вопросе. Первое, что мы делаем, – это получаем экземпляр класса MQTTClient. Мы передаем имя хоста сервера (в данном случае io.adafruit.com), идентификатор пользователя, ключ IO и порт. Получив экземпляр клиента, мы можем установить обратный вызов для получения любых сообщений из наших подписанных каналов, связанных с идентификатором нашего устройства. Для этого мы используем функцию set_callback(), передавая имя нашего метода для обработки сообщений. Напомним, это message_callback().
Проект: погода IoT  439 Далее мы указываем канал, используя параметр темы. Названия каналов всегда формируются с использованием идентификатора пользователя. Вы можете представлять себе это как файловый путь. Обязательно внимательно проверьте названия каналов, чтобы убедиться, что вы выбрали правильное. Вы всегда можете проверить свою страницу Adafruit IO, чтобы убедиться в правильности названий. Далее мы пишем цикл while для чтения данных с датчиков. Чтобы упрос­ тить задачу, мы будем выполнять бесконечный цикл (но вы можете изменить это, если планируете сделать проект не просто экспериментом). Мы читаем данные с помощью функции read_data(), затем публикуем каждый элемент данных в соответствующем канале с помощью функции client.publish(). Например, чтобы отправить данные в канал температуры, мы указываем идентификатор пользователя и канал с помощью параметра темы и отправляем данные через параметр msg следующим образом: client.publish(topic="{0}/feeds/temperature".format(self.io_user), msg=str(data[0])) В конце цикла мы делаем несколько вещей. Мы уводим контроллер в сон на количество секунд, указанное в параметре частоты обновления. Таким образом мы можем избежать слишком частой отправки слишком большого количества данных и организовать сбор данных с реалистичным интервалом. Далее мы проверяем наличие сообщений с помощью функции client. check_msg(). Эта функция просто проверяет, поступило ли какое-либо новое сообщение. Если они есть, поскольку мы указали функцию обратного вызова, функция обратного вызова будет вызываться для каждого нового сообщения. Как видите, опросы не нужны. В лис­тин­ге 14.2 показан готовый код класса датчика погоды. Листинг 14.2  Класс датчика погоды (weather_sensor_iot.py) # # MicroPython for the IoT Second Edition - Chapter 14 # # Chapter 14 - BME280 Weather Class with MQTT and Adafruit IoT # # Required Components: # - (1) BME280 Weather Sensor # # Imports for the project import time from umqtt.simple import MQTTClient from project6.bme280 import BME280 class WeatherSensorIoT: """Sensor node using a BME280 sensor to send temperature, humidity, and barometric pressure to io.adafruit.com MQTT broker.""" # Constructor def __init__(self, i2c, io_user, io_key, frequency, port=1883):
440  MQTT с Adafruit IO # Save variables passed for use in other methods self.io_user = io_user self.io_key = io_key self.update_frequency = frequency self.port = port # Now, setup the sensor self.sensor = BME280(i2c=i2c, address=0x77) time.sleep(0.100) print("Weather MQTT client is ready.") # Reads the sensor. Returns a tuple of (temperature, humidity, pressure) def read_data(self): time.sleep_ms(50) raw_values = self.sensor.raw_values temperature = raw_values[0] humidity = raw_values[2] pressure = raw_values[1] print("Data read:", self.sensor.values) return (temperature, humidity, pressure) # A simple callback to print the message from the server def message_callback(self, topic, msg): print("[{0}]: {1}".format(topic, msg)) def run(self): # Now we setup our MQTT client client = MQTTClient("WeatherIoT", "io.adafruit.com", user=self.io_user, password=self.io_key, port=self.port) client.set_callback(self.message_callback) client.connect() while True: data = self.read_data() client.publish(topic="{0}/feeds/temperature".format( self.io_user), msg=str(data[0])) client.publish(topic="{0}/feeds/humidity".format( self.io_user), msg=str(data[1])) client.publish(topic="{0}/feeds/pressure".format( self.io_user), msg=str(data[2])) time.sleep(self.update_frequency) client.check_msg() Уделите несколько минут изучению кода, пока не убедитесь, что поняли, как он работает. Теперь, когда у нас есть класс датчика, мы можем написать основной код. Основной код Основной файл кода называется weather_iot.py. Как вы увидите, сам код очень короткий и простой, потому что вся работа выполняется в нашем модуле
Проект: погода IoT  441 класса! В следующих разделах кратко описаны основные части основного кода, начиная с импорта. Поскольку вся работа выполняется в классе WeatherSensorIoT, нам нужно только импортировать этот модуль кода (weather_sensor_iot) и те библиотеки, которые нам нужны для подключения платы к нашей сети Wi-Fi, следующим образом: import network, sys, time from machine import Pin, SoftI2C from project6.weather_sensor_iot import WeatherSensorIoT from project6.secrets import SSID, SSID_PASS, IO_USER, IO_KEY Еще нам нужна одна константа для частоты обновлений. Мы будем использовать консервативные 30 секунд для тестирования, но вы можете увеличить промежуток, когда введете этот проект в эксплуатацию, чтобы избежать загрузки слишком большого количества данных. FREQUENCY = 30 # seconds Мы будем использовать ту же функцию connect(), что и в предыдущих главах, но изменим ее, чтобы использовать переменные SSID и SSID_PASS из файла secrets.py (см. полный код далее). Функция main() очень проста. Все, что нам нужно сделать, – это вызвать функцию сonnect() для подключения к сети Wi-Fi, настроить BME280, используя тот же код из главы 11, который позволяет применять Pico или Nano RP2040 Connect, затем создать экземпляр класса WeatherSensorIoT и, наконец, вызвать функцию run() для этого класса. Вот и все! В лис­тин­ге 14.3 показан готовый код модуля основного кода мониторинга погоды (weather_iot.py). Листинг 14.3  Основной код (weather_iot.py) # # MicroPython for the IoT Second Edition - Chapter 14 # # Chapter 14 - MicroPython Weather IoT with MQTT and Adafruit IO # # Required Components: # - (1) BME280 Weather Sensor # # Imports for the project import network, sys, time from machine import Pin, SoftI2C from project6.weather_sensor_iot import WeatherSensorIoT from project6.secrets import SSID, SSID_PASS, IO_USER, IO_KEY FREQUENCY = 30 # seconds # Setup the board to connect to our network. def connect():
442  MQTT с Adafruit IO wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(SSID, SSID_PASS) while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect...") time.sleep(1) if not wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(wlan.ifconfig()[0])) return True # Main function def main(): # Setup the socket and respond to HTML requests # Connect to the network if not connect(): sys.exit(-1) # # Arduino Nano RP2040 Connect uses sda=12, scl=13 #sda = Pin(12) #scl = Pin(13) # # Raspberry Pi Pico uses sda=8, scl=9 sda = Pin(8) scl = Pin(9) # # Software I2C (bit-banging) for the RTC i2c = SoftI2C(sda=sda, scl=scl, freq=100000) # Run the weather MQTT client weather_mqtt_client = WeatherSensorIoT(i2c, IO_USER, IO_KEY, FREQUENCY) weather_mqtt_client.run() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Теперь давайте запустим этот проект! Выполнение кода Теперь самое интересное! У нас есть код, настроенный для считывания данных о погоде с датчика и публикации данных в Adafruit IO. Сначала скопируем нужные файлы на плату MicroPython. Вам нужно будет создать на плате папку с именем project6 и скопировать туда файлы secrets.
Выполнение кода  443 py и weather_sensor_iot.py. В ту же папку project6 на плате также необходимо скопировать библиотеку bme280.py из главы 11. Наконец, вы можете скопировать файл weather_iot.py в корневую папку на плате. При работе с данными в ваших каналах следует учитывать еще одну вещь, касающуюся настройки Show History (Показать историю). Вы можете изменить это значение в соответствии с вашими ожиданиями относительно данных. Использование параметра Live (В реальном времени) лучше всего позволяет просматривать данные в момент их записи, но если вы хотите просмотреть данные за прошлые периоды времени, вам следует выбрать один из других вариантов. Например, вы можете выбрать просмотр данных за последний час. Чтобы изменить настройку Show History, щелкните значок шестеренки для блока, который вы хотите изменить, затем щелкните раскрывающийся список, как показано на рис. 14.21. Для тестирования наших проектов лучше всего подходит один час. Рис. 14.21  Изменение параметра Show History для блока После изменения параметра истории можно выполнить код на плате MicroPython. В лис­тин­ге 14.4 показан пример вывода результата, который вы должны получить. Листинг 14.4  Пример результирующего вывода Connected! My IP address is: 10.0.0.14 Weather MQTT client is ready. Data read: ('20.3C', '993.96hPa', '63.00%') Data read: ('20.13C', '994.01hPa', '63.49%') Data read: ('19.97C', '993.95hPa', '63.92%') Data read: ('19.92C', '993.97hPa', '64.20%') Data read: ('19.86C', '993.93hPa', '64.39%')
444  MQTT с Adafruit IO Data Data Data Data ... read: read: read: read: ('19.8C', '994.05hPa', '64.52%') ('19.76C', '993.88hPa', '64.66%') ('19.71C', '993.96hPa', '64.77%') ('19.66C', '993.93hPa', '64.86%') Поскольку частота отбора установлена на 30 секунд, вам следует дать программе поработать несколько минут, прежде чем просматривать информационную панель. Когда вы это сделаете, вы должны увидеть данные, аналогичные рис. 14.22. Рис. 14.22  Пример информационной панели с данными Если вы видите что-то подобное, то вы сделали все правильно! Если нет, обязательно выявите проблемы с кодом, устраните их и дайте программе поработать примерно 10–15 минут. Обратите внимание, что линии давления и влажности меняются не сильно. Это ожидаемо, поскольку этот пример запускался в помещении в течение очень короткого времени. Тем не менее вы можете заметить повышение и падение данных о температуре. Это произошло из-за искусственного изменения температуры. То есть я переместил источник тепла ближе к датчику, чтобы сделать область теплее и, таким образом, записать более высокие значения. Аналогичным образом я использовал источник прохладного воздуха для охлаждения области, что привело к падению значений. Одновременно
А что дальше  445 это вызвало небольшое изменение значений влажности. Можете ли вы ответить, почему это произошло1? Вы можете попробовать это сами, но будьте осторожны! Не прикасайтесь к датчику, чтобы не повредить его, и не используйте открытое пламя или другие источники тепла, которые могут вызвать ожоги. ОСТОРОЖНО Не поддавайтесь искушению прикоснуться к датчику. Вы можете повредить датчик2. Если вам понравился этот проект, вы можете рассмотреть возможность его выполнения в течение более длительного периода времени, увеличив промежуток между выборками до нескольких часов. Вы по-прежнему будете видеть данные, и если температура в вашей среде изменится, вы увидите фактическое изменение данных, а не смоделированные вручную изменения. На этом этапе вы завершили еще один настоящий IoT-проект MicroPython. В данном случае мы увидели проект, который собирает и отображает данные в облаке. А что дальше? Этот проект показывает отличные перспективы для повторного использования методик в других проектах. Это особенно актуально теперь, когда вы знаете, как использовать MQTT. Если вам понравилось просматривать данные своих датчиков через интернет, вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые вы можете рассмотреть:   добавьте больше датчиков, чтобы расширить свой проект и включить в него больше наблюдений за погодой;   измените информационную панель для отображения данных с использованием разных блоков; 1 2 Это произошло потому, что величина относительной влажности связана с температурой: при одинаковом абсолютном влагосодержании в воздухе (в граммах на куб. м) при снижении температуры относительная влажность будет увеличиваться, вплоть до достижения точки росы (влажность 100 %), которая при указанных на графиках значениях (70 % влажности при приблизительно 20 градусах) наступит при достижении около 10–12 градусов. – Прим. перев. BME280 (как и более простые его аналоги среди датчиков температуры-влажности и давления) защищен достаточно, чтобы не бояться простого прикосновения. Его вполне можно испытывать на нагревание, зажав между пальцами (разумеется, чистыми и сухими). Но все эти датчики (особенно влажностный сенсор) совершенно не переносят загрязнений: даже случайная заливка водой или техническими жидкостями может их повредить. – Прим. перев.
446  MQTT с Adafruit IO   добавьте больше узлов датчиков для сбора данных наблюдений из других мест, создавая для каждого новые каналы;   настройте свой собственный MQTT-брокер и подключите свои сенсорные узлы. Итоги Использование облачных сервисов – это следующий шаг для ваших IoTпроектов, предоставляющий безграничные возможности для хранения и обмена данными с другими пользователями, компаниями и иными облачными сервисами. Имея так много доступных возможностей, вы можете делать со своим проектом интернета вещей практически все, и я призываю вас изучить эти возможности. В данной главе мы продемонстрировали этот подход, создав проект датчика погоды, который подключил нашу плату к облачному сервису Adafruit IO и использовал этот сервис для хранения и визуализации данных. Хотя этот проект кажется проще, чем предыдущие проекты, в последних мы получили специальные знания, которые облегчили реализацию данного проекта. То есть, не обладая базовыми знаниями о том, как создавать IoT-проекты, углубляться было бы глупостью. С помощью замечательных ребят из Adafruit мы теперь можем создавать IoT-проекты для отправки данных в облако и обмена ими со всем миром. Мало того, мы также можем создавать решения, которые позволяют нам управлять проектами из интернета (подсказка: используйте функцию subscribe()). Все это делается быстро, с минимальными усилиями и без необходимости изучать сложный API. В следующей главе мы увидим, как один из наших предыдущих проектов повышен до уровня IoT-проекта с использованием ThingSpeak в качестве поставщика облачных услуг. Как вы увидите, это позволит нам управлять платой MicroPython из интернета.
Глава 15 Сервис ThingSpeak Теперь, когда мы накопили хороший опыт работы с несколькими проектами, в том числе с теми, которые используют облачные сервисы, мы готовы создать наш последний проект интернета вещей для MicroPython, который использует более продвинутые облачные сервисы. В этой главе мы узнаем, как использовать облачную службу размещения данных IoT от MathWorks под названием ThingSpeak (www.thingspeak.com). Мы увидим, как взять наш пример проекта мониторинга растений и подключить его к ThingSpeak. Мы создадим проект облачного мониторинга, с помощью которого вы сможете сразу увидеть значение влажности почвы через интернет. Обзор Вспомните из главы 12, что ThingSpeak – это еще одна облачная служба, похожая на MQTT, которая позволяет хранить ваши данные в облаке с помощью популярной и простой в использовании службы размещения данных от MathWorks. ThingSpeak предлагает более надежный набор инструментов для создания информационных IoT-панелей, чем большинство других облачных сервисов. Как вы увидите в этой главе, проект, который мы создадим, будет использовать четыре элемента информационной панели (называемые здесь «визуализациями»), которые вы можете настроить для отображения данных в практически бесконечном количестве вариантов форматов. Возможно, лучше всего – это простота отправки данных в ThingSpeak. Как вы увидите, для этого требуется простое HTTP-сообщение, отправленное на сервер ThingSpeak, где мы указываем ключ API и список полей для обновления. Таким образом, ThingSpeak для проектов интернета вещей – один из самых простых в использовании. Давайте посмотрим, как начать работу с ThingSpeak, включая более глубокое изучение того, как создать класс для подключения к ThingSpeak, который вы сможете повторно использовать в будущих проектах.
448  Сервис ThingSpeak Начало работы с ThingSpeak Мы не будем слишком углубляться в детали протоколов, необходимых для связи с ThingSpeak; скорее, мы представим краткое руководство по использованию ThingSpeak. MathWorks предоставляет полный набор учебных пособий, документации и примеров. Если вам нужна дополнительная информация о том, как работает ThingSpeak, ознакомьтесь с документацией по адресу www.mathworks.com/help/thingspeak/. Чтобы настроить ваш проект для использования ThingSpeak, необходимо выполнить несколько следующих шагов:   создайте учетную запись (если у вас ее еще нет);   создайте канал;   получите ключи API. Начнем с создания учетной записи в ThingSpeak. Создание учетной записи в ThingSpeak Чтобы использовать ThingSpeak, вам необходимо сначала зарегистрировать учетную запись. К счастью, существует возможность создать бесплатную учетную запись. Фактически вы получаете бесплатную учетную запись для начала и добавляете (приобретаете) лицензию позже. Чтобы создать бесплатную учетную запись, посетите https://thingspeak.com/, нажмите Get Started For Free (Начать бесплатно), затем нажмите Create (Создать), как показано на рис. 15.1. Рис. 15.1  Создание новой учетной записи ThingSpeak/MathLabs На следующей странице введите свой адрес электронной почты, местоположение (общегеографическое), а также имя и фамилию, затем нажмите Continue (Продолжить). Вам будет отправлено электронное письмо с подтверждением. Откройте его и следуйте инструкциям, чтобы подтвердить
Начало работы с ThingSpeak  449 свой адрес электронной почты и заполнить бесплатную учетную запись, выбрав пароль. Вас могут попросить заполнить небольшую анкету. Обязательно войдите в систему, прежде чем продолжить. Далее создадим наш первый канал. Создание канала После входа в ThingSpeak вы можете создать канал для хранения ваших данных. Напомним, каждый канал может иметь до восьми элементов данных (полей). На домашней странице входа нажмите New Channel (Новый канал), как показано на рис. 15.2. Рис. 15.2  Создание канала в ThingSpeak Вам будет представлена очень длинная форма со множеством полей, которые вы должны заполнить. На рис. 15.3 показан пример формы. Как минимум вам нужно назвать канал, ввести описание (не строго обязательно, но рекомендуется), а затем выбрать (отметить) одно или несколько полей с названием для каждого. Итак, что же это за настройки канала? Ниже дается краткий обзор каждого из них. Работая с ThingSpeak, вы можете начать использовать некоторые из этих полей:   Percentage complete (Процент выполнения): вычисляемое поле, основанное на заполнении имени, описания, местоположения, URL-адреса, видео и тегов на вашем канале;   Channel Name (Имя канала): уникальное имя канала;   Description (Описание): описание канала;   Field# (Номер поля): отметьте номером каждое поле, чтобы включить его;   Metadata (Метаданные): дополнительные данные для канала в формате JSON, XML или CSV;   Tags (Теги): список ключевых слов для поиска, разделенных запятыми;   Link to External Site (Ссылка на внешний сайт). Если у вас есть веб-сайт вашего проекта, вы можете указать здесь URL-адрес для публикации на канале;   Show Channel Location (Показать местоположение канала): установите этот флажок, чтобы включить следующие поля:
450  Сервис ThingSpeak Рис. 15.3  Форма нового канала – Latitude (Широта): географическая широта датчиков проекта или источника данных; – Longitude (Долгота): долгота датчиков для проекта или источника данных; – Elevation (Высота): высота в метрах для использования в проектах, на которые влияет высота;   Video URL: если у вас есть видео, связанное с вашим проектом, вы можете указать здесь URL-адрес для публикации на канале;
Начало работы с ThingSpeak  451   Link to GitHub (Ссылка на GitHub): если ваш проект размещен на GitHub, вы можете указать URL-адрес для публикации на канале. Ого, сколько всего здесь предоставляется бесплатно! Как вы увидите, это не простая игрушка или строго ограниченный продукт. С этими настройками вы можете добиться довольно многого. Обратите внимание, что есть поля для размещения ссылок на видео, веб-сайт и GitHub. Это связано с тем, что каналы могут быть либо частными (доступ к которым, как мы увидим, может иметь только ваш логин или ключ API), либо публичными. Публичность канала позволяет вам делиться данными с кем угодно, и поэтому эти поля URL могут быть полезны для документирования вашего проекта. Отлично! Теперь давайте создадим практический канал, который мы будем использовать в следующем разделе, чтобы проиллюстрировать, как записывать (загружать – upload) данные в ThingSpeak. Используйте следующие параметры для полей формы New Channel (Новый канал):   имя: practice_channel;   описание: Practice channel for testing MicroPython 1;   метка поля 1: RandInt. ПРИМЕЧАНИЕ Имена полей, которые мы вводим при настройке канала, предназначены только для презентационных целей (это метки). На поля ссылается последовательность форм fieldN, начиная с 1 (например, field1, см. рис. 15.3). Мы увидим это по мере продвижения вперед. Введите значения, как показано, затем нажмите Save Channel (Сохранить канал), чтобы завершить процесс. Теперь мы готовы протестировать запись некоторых данных. Получение ключа API После того как вы создали свой канал, пришло время записать некоторые данные. Для большинства проектов вам понадобятся две единицы информации: ключ API для канала и для некоторых библиотек номер канала (целочисленное значение, отображаемое на странице канала). Существуют биб­лио­ те­ки, доступные для многих платформ, а на некоторых платформах может существовать несколько способов (библиотек или методов) записи данных в канал ThingSpeak. Ключ API можно найти на странице канала, перейдя на вкладку API Keys (Ключи API). Когда вы создаете новый канал, у вас будет один ключ API для записи и один для чтения. Вы можете добавить больше ключей, если они вам нужны, чтобы вы могли использовать один ключ для каждого устройства, местоположения, клиента и т. д. 1 Сайт ThingSpeak должен поддерживать Unicode, потому возможно введение описания по-русски («Практический канал по тестированию MicroPython»). Но это не касается, конечно, названия канала или меток, которые лучше вводить по-анг­лий­ ски. – Прим. перев.
452  Сервис ThingSpeak Если вы сделаете свой канал общедоступным, не сообщайте ключ записи никому, кому вы не хотите разрешать выводить данные на свой канал. Вы можете создать новые ключи, нажав кнопку New Write API Key (Создать новый ключ API записи) или Add New Read API Key (Добавить новый ключ API чтения). Вы можете удалить прочитанные ключи из доступа, нажав кнопку Delete API Key (Удалить ключ API). Вы также можете добавить примечание, чтобы напомнить вам о любых соответствующих данных, которые вы, возможно, захотите вспомнить позже. На рис. 15.4 показана вкладка API Keys (Ключи API) для созданного ранее канала. Рис. 15.4  Ключи API для канала practice_channel Обратите внимание, что я замаскировал поле ключей. Мы используем ключ в коде, чтобы позволить устройству подключаться к каналу и записывать данные в него. Обычно мы копируем эту строку со страницы канала и вставляем ее в код как строку. Теперь, когда вы понимаете основы записи данных в ThingSpeak, давайте рассмотрим более сложную конфигурацию, которая поможет вам повторно использовать большую часть того, что необходимо для включения ThingSpeak в будущие проекты. Использование ThingSpeak с MicroPython Еще раз: этот проект представляет собой очень простой набросок, позволяющий научиться подключаться и записывать данные в канал ThingSpeak. Для данных мы будем генерировать случайное целое число и отправлять его в канал.
Начало работы с ThingSpeak  453 Аппаратное обеспечение, которое мы будем использовать, – это просто наша плата MicroPython (Pico или Nano RP2040 Connect). Никакого дополнительного оборудования не требуется. Однако вы можете рассмотреть возможность установки свежего образа на плату MicroPython. СОВЕТ При запуске нового проекта, который не основан на предыдущих проектах, рекомендуется установить новый образ MicroPython (файл .uf2), чтобы начать с заведомо исправной базовой версии. Вот и все! Теперь давайте напишем код. Как вы увидите, в ThingSpeak используется другой механизм загрузки данных. Это потому, что у нас нет класса для представления связанных с этими действиями функций, но мы его создадим! Написание кода Для отправки сообщения в ThingSpeak через HTTP мы будем использовать библиотеку urequests. Мы создадим новый класс, который сможем использовать в других проектах, и, чтобы упростить задачу, переместим в этот класс нашу функцию connect() для Wi-Fi. Это сделает наш основной код намного короче, и вам будет легче сосредоточиться на общей картине того, что делает программа. Файл класса, который мы создадим, будет называться thingspeak.py и содержать класс ThingSpeak. Для большинства примеров, в которых вы будете использовать этот класс, нам понадобится только конструктор, наша функция connect() для Wi-Fi и функция для записи (загрузки) данных в ThingSpeak. Чтобы сделать программу более устойчивой к сетевым проблемам, в процедуру загрузки мы также встроим цикл повтора. Начнем с импорта и констант. Нам нужно импортировать библиотеки network, time и urequests. Нам также нужны значения из файла secrets.py, как мы использовали в предыдущих проектах, но с одним дополнительным значением: нашим ключом записи API для ThingSpeak. Мы будем использовать две константы: значение максимального количества повторов и строку HTTPзаголовка, который мы отправим в ThingSpeak. Идея заключается в том, что загрузка будет повторяться до максимального значения MAX_RETRIES, прежде чем будет прервана. Это поможет, когда плата подключена к медленной или сбойной сети. import network, time, urequests from project7.secrets import SSID, SSID_PASS, THINGSPEAK_WRITE_APIKEY # Constants MAX_RETRIES = 10 HTTP_HEADERS = {"Content-Type": "application/json"} ... Для конструктора мы примем настроенное пользователем максимальное количество повторов со значением по умолчанию MAX_RETRIES. Мы также на-
454  Сервис ThingSpeak строим класс WiFi, как и в предыдущих проектах, просто скопировав функцию connect() из предыдущих проектов. Для функции upload_data() нам потребуется словарь Python, включающий все ключи и их значения. Нам нужно добавить ключ API, но мы можем сделать это легко. В функции мы создадим цикл, содержащий блок try … except для вызова сетевых функций, которые мы будем использовать. В частности, мы открываем соединение с сервером ThingSpeak, выдаем запрос POST, а затем ждем кода состояния (status code). Обратите внимание, что мы используем функцию urequests.post() для отправки URL-адреса, который представляет собой URL-адрес ThingSpeak плюс наш ключ API записи, полезные данные (словарь пар ключ–значение) и заголовок, который мы создали как константу. Затем мы проверяем код состояния, чтобы убедиться, что загрузка работает. Код состояния 200 означает успех, но если мы получим что-нибудь еще, то распечатаем данные ответа (заголовки, причину и код статуса) для последующего анализа. Если вы видите причину, код возврата или какие-либо поля в ответном пакете со значением 6, это, скорее всего, означает, что у вас проблема с форматированием словаря значений данных. ThingSpeak «подсчитывает» ключи полей, начиная с единицы (field1, field2, field3 и т. д.), соответствующие полям, которые вы определили, но не метке, которую вы им назначили. Если вы получаете странные коды отклика или не видите никаких данных на панели управления, проверьте ключи словаря. СОВЕТ Если вы не получили код состояния 200 (что означает «ОК»), проверьте словарь представленных данных, чтобы убедиться, что ключи и значения отформатированы правильно. Если программа столкнулась с проблемой в какой-либо из сетевых функций, она «засыпает» на пять секунд, затем пробует команду еще раз. Мы будем делать это до MAX_RETRIES раз или до тех пор, пока операция не завершится успешно. В лис­тин­ге 15.1 показан полный код этого класса. Уделите некоторое время прочтению и ознакомлению с тем, как это работает. Листинг 15.1  Класс ThingSpeak # # MicroPython for the IoT Second Edition - Chapter 15 # # MicroPython IoT Plant Monitoring with ThingSpeak # # ThingSpeak class with networking # # Import libraries import network, time, urequests from project7.secrets import SSID, SSID_PASS, THINGSPEAK_WRITE_APIKEY # Constants
Начало работы с ThingSpeak  455 MAX_RETRIES = 10 HTTP_HEADERS = {"Content-Type": "application/json"} class ThingSpeak: """This class permits you to upload data to ThingSpeak using your API write key.""" # Constructor def __init__(self, num_retries=MAX_RETRIES): self.api_key = THINGSPEAK_WRITE_APIKEY self.max_retries = num_retries self.wlan = network.WLAN(network.STA_IF) self.wlan.active(True) # Setup the board to connect to our network. def connect(self): self.wlan.connect(SSID, SSID_PASS) while not self.wlan.isconnected() and self.wlan.status() >= 0: print("Waiting to connect...") time.sleep(1) if not self.wlan.isconnected(): print("Cannot find SSID!") sys.exit(0) print("Connected!") print("My IP address is: {0}".format(self.wlan.ifconfig()[0] )) return True # Upload the data in the form of a dictionary of key, # values to ThingSpeak. def upload_data(self, param_dict, timeout=5000): # Make sure we're connected to the WiFi if not self.wlan.isconnected(): if not connect(): return False # Attempt to retry if the timeout fails retry = 0 while retry <= self.max_retries: try: print("Sending data...", end="") response = urequests.post( "http://api.thingspeak.com/update?api_key=" + self.api_key, json=param_dict, headers=HTTP_HEADERS, ) break except Exception as err: print("\nWARNING: Update failed: {0}".format(err)) if retry <= self.max_retries: print("Retrying in 5 seconds. [{}]".format(retry +1)) time.sleep(5) retry = retry + 1
456  Сервис ThingSpeak else: retry = self.max_retries + 1 print("WARNING: Cannot upload to ThingSpeak. " "Exceeded number of retries. Abort.") response.close() return False # Check header for correct status. if response.status_code == 200: print("done.") else: print("\nWARNING: data not acknowledged.") print("Header: {0}\nStatus: {1}\nReason: {2}" "\n".format(response.headers, response.status_code, response.reason.decode('ascii'))) response.close() return True Далее приведем код основного скрипта. Мы будем использовать новый класс для загрузки сгенерированного нами случайного числа. Назовем основной скрипт test_thingspeak.py. Если вы следуете этому примеру, откройте новый файл с этим именем. Как и в наших предыдущих проектах, мы создадим новую папку проекта на плате MicroPython (project7) и скопируем туда наши библиотеки. Обязательно поместите модуль thingspeak.py в каталог python7 перед запуском тестового кода. В лис­тин­ге 15.2 показан полный код сценария для этого проекта. Это следует уже знакомому шаблону, где мы создаем функцию main() и вызываем ее из блока try…except, чтобы поймать последовательность клавиш <CTRL>+<C>. Код очень прост. Все, что вам нужно сделать, – это поместить ключ API в константу и запустить его. Листинг 15.2  Полный код скрипта test_thingspeak.py # # MicroPython for the IoT Second Edition - Chapter 15 # # Chapter 15 - IoT Plant Monitoring with ThingSpeak # # Test ThingSpeak # # Import libraries import random, sys, time from project7.thingspeak import ThingSpeak # Run the program to upload random data to ThingSpeak def main(): print("Welcome to the ThingSpeak using MicroPython demonstration!") print("Press CTRL+C to stop.")
Начало работы с ThingSpeak  457 thing_speak = ThingSpeak() thing_speak.connect() while True: # Generate a random integer rand_int = random.randint(1, 20) print("Random number generated: {}".format(rand_int)) thing_speak.upload_data({'field1': rand_int}) # Sleep for 30 seconds time.sleep(10) if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Обратите внимание на словарь, который мы использовали для передачи данных в функцию upload(). Здесь мы использовали поле field1 в качестве ключа для поля канала. Как оказалось, мы должны использовать field1, field2 и т. д. для имен ключей полей независимо от того, как мы можем назвать их в канале. Хотя это может показаться немного странным, вам следует выработать привычку перечислять поля словаря в том порядке, в котором они появляются в настройке канала. ПРИМЕЧАНИЕ Обязательно замените ключ API в нужном месте1. Невыполнение этого требования приведет к ошибкам во время выполнения. Теперь, когда вы ввели весь код, давайте проверим его и посмотрим, работает ли он. Выполнение кода Напомним, что мы можем запустить код из Thonny, или вы можете подключиться к плате MicroPython через REPL и выполнить код из консоли REPL. В лис­тин­ге 15.3 показан пример вывода результатов, который вы должны получить. Возможно, вы захотите дать коду поработать несколько минут, чтобы сгенерировать несколько значений. Дайте ему поработать, чтобы сгенерировалось около 20 значений, прежде чем проверять канал ThingSpeak на наличие данных. Листинг 15.3  Пример вывода результатов примера (Python) Welcome to the ThingSpeak using MicroPython demonstration! Press CTRL+C to stop. Connected! My IP address is: 10.0.0.14 1 Автор, очевидно, имеет в виду файл secrets.py, который здесь не приведен полностью (см. начало раздела «Написание кода»). – Прим. перев.
458  Сервис ThingSpeak Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. Random number generated: Sending data...done. ... Sending data...done. 15 18 13 17 19 16 4 16 bye! Дайте скетчу поработать несколько минут, прежде чем посетить ThingSpeak. После того как программа проработает некоторое время, перейдите на ThingSpeak, войдите в систему и щелкните страницу своего канала, затем перейдите на вкладку Private View (Приватный просмотр). Мы используем приватный доступ, поскольку каналы по умолчанию являются закрытыми для всеобщего доступа. Вы должны увидеть результаты, аналогичные показанным на рис. 15.5. Рис. 15.5  Данные канала practice_channel Если вы не видите подобных данных, вернитесь и проверьте коды отклика, как обсуждалось в предыдущем проекте. Вы должны увидеть код отклика 200
Проект: IoT-мониторинг растений  459 (что означает «все отлично»). Проверьте все и исправьте ошибки в сетевом подключении, а также синтаксические или логические ошибки в сценарии до тех пор, пока он не запустится успешно в течение нескольких итераций (во всех примерах сохраняется код возврата 200). Если вы видите подобные данные, поздравляем! Теперь вы знаете, как генерировать данные и сохранять их в облаке, используя две разные платформы. Сейчас давайте обратим внимание на один из наших предыдущих примеров проектов, переработав его для загрузки данных в ThingSpeak. ПРИМЕЧАНИЕ Бесплатные учетные записи ThingSpeak ограничены четырьмя каналами. Если вы планируете реализовать все примеры проектов в этой главе, вам может потребоваться удалить один или несколько каналов или перевести свою учетную запись на платную подписку. Чтобы удалить канал, на домашней странице вы можете перейти на вкладку Settings (Настройки), перейти к настройкам канала, прокрутить вниз и нажать Delete Channel (Удалить канал). Теперь давайте рассмотрим проект интернета вещей, который улучшает наше решение для мониторинга растений. Проект: IoT-мониторинг растений Давайте возьмем проект из главы 10 и превратим его в настоящий IoT-проект. Мы добавим код ThingSpeak, который тестировали в предыдущем разделе, для отправки данных в облако. Начнем с создания нового канала ThingSpeak. Создание канала Данные для этого проекта немного отличаются, потому что, хотя у нас есть значения от датчиков почвы, они на самом деле ничего не значат, пока мы не откалибруем их, чтобы определить диапазон для влажной, нормальной и сухой почвы. Напомним, у нас может быть один или несколько датчиков почвы, и мы генерируем оценку влажности для каждого. В этом примере мы будем собирать необработанные данные, а не категории, полученные в результате калибровки. Таким образом, нам понадобится один канал с одним полем для каждого датчика. Для простоты, как и раньше, будем использовать только два датчика, но напишем код, позволяющий использовать большее количество. Мы создадим канал с двумя полями, названными согласно каждому из датчиков. Для ясности будем использовать название растений, которые эти датчики отслеживают. Войдите в свою учетную запись ThingSpeak и нажмите New Channel (Новый канал). Назовем канал Plants IoT или IoT Plants (или как хотите). Используйте информацию, показанную на рис. 15.6, чтобы заполнить форму,
460  Сервис ThingSpeak затем нажмите Save Channel (Сохранить канал) внизу формы (вы можете также просто нажать <Enter>, чтобы сохранить канал). Обратите внимание, что вам нужно будет установить флажки для полей 1 и 2, чтобы они могли принимать входные параметры. Рис. 15.6  Настройки канала Plants IoT Напомним, нам нужно запомнить порядок полей. Здесь мы определили Tomato и FreddyFern1, в нашем коде они будут называться field1 и field2 соответственно. Настройка ThingSpeak Следующим шагом в настройке канала ThingSpeak является создание визуализаций и виджетов для представления данных. Напомним, у нас есть датчики, выдающие целые числа, которые сами по себе не очень помогают определить, нужно ли растению больше или меньше воды. Мы создадим визуальное представление необработанных данных датчиков и, что более интересно, визуализацию, которая сразу показывает нам, нуждается ли растение в поливе. Таким образом, нам понадобятся две визуализации для необработанных данных и два виджета, которые отображают влажное, нормальное и сухое состояния. Создание визуализаций На странице вашего канала перейдите на вкладку Private View (Приватный просмотр). Вы должны увидеть одну визуализацию в виде графика, которая является представлением по умолчанию. Щелкните значок карандаша, как показано на рис. 15.7, чтобы ее отредактировать. 1 Fern – папоротник. Далее у автора растение называется просто fern. – Прим. перев.
Проект: IoT-мониторинг растений  461 Рис. 15.7  Редактирование визуализации Далее нужно изменить конфигурацию визуализации, как показано на рис. 15.8, затем нажать Save (Сохранить). Рис. 15.8  Настройка графика для поля Field 1
462  Сервис ThingSpeak Затем создайте новую визуализацию, нажав Add Visualizations (Добавить визуализации), потом выберите график с именем Custom (no start code) (Пользовательская без начального кода) и нажмите Save (Сохранить), как показано на рис. 15.9. Рис. 15.9  Выбор представления для поля Field 2 Затем отредактируйте новую визуализацию и измените ее конфигурацию, как показано на рис. 15.10, далее нажмите Save (Сохранить). Рис. 15.10  Настройка графика для поля Field 2
Проект: IoT-мониторинг растений  463 Теперь создадим виджеты. Создание виджетов Нам нужен какой-то способ визуализировать данные от наших датчиков, которые показывают влажное, нормальное и сухое состояния. Хорошим виджетом для этой цели является круговая шкала (gauge), поскольку она позволяет создавать диапазоны с цветовой кодировкой. Таким образом, мы можем создать диапазон для сухих (при низких значениях), для нормальных и еще один для влажных состояний. Мы создадим две шкалы, по одной для каждого поля. Чтобы создать виджет, нажмите кнопку Add Widgets (Добавить виджеты), затем выберите виджет «Gauge» и нажмите Next (Далее), как показано на рис. 15.11. Рис. 15.11  Добавление виджета (Gauge) Далее мы настроим параметры виджета. Здесь мы хотим установить диапазоны данных для показаний шкалы, используя значения, которые мы на­ шли при калибровке датчиков. Обратитесь к главе 10, чтобы узнать, как это сделать, если вы еще этого не сделали. Мы можем установить галочки, метки и многое другое! Наконец, нам также необходимо настроить цветовые диапазоны для шкалы. Мы создадим три диапазона. Диапазон сухого состояния при моей калибровке составлял 0–700. Нормальный диапазон нормального состояния составлял 701–2500, а влажного – более 2500. Вы можете добавить диапазоны в нижней части диа-
464  Сервис ThingSpeak логового окна, нажав кнопку +. Я выбираю красный, зеленый и оранжевый цвета для сухого, нормального и влажного состояний. На рис. 15.12 показана установка шкалы для первого датчика (поле Field 1). Внесите изменения и нажмите Save (Сохранить). Рис. 15.12  Настройка шкалы для поля Field 1 Размер следующего поля очень похож, за исключением меток. Добавьте новый датчик и затем настройте его, как показано на рис. 15.13. Создав две визуализации и два виджета, вы можете перемещать их по панели мониторинга. Я поместил шкалу для поля Field 1 под график для поля Field 1 и то же самое для поля Field 2. Возможно, вы захотите, чтобы датчики находились над необработанными данными, или после тестирования проекта вы можете удалить графики, чтобы сделать интерфейс более компактным. В конце концов, вам в действительности нужны только шкалы.
Проект: IoT-мониторинг растений  465 Рис. 15.13  Настройка шкалы для поля Field 2 После того как вы настроите информационную панель, вы должны увидеть два графика и две шкалы в макете, аналогичном рис. 15.14. Обратите внимание, что в канале еще нет данных, поэтому шкалы датчиков не будут выглядеть нормально. Вы можете также увидеть сообщение об ошибке, что поле недоступно. Можете пока это игнорировать. Теперь, когда у нас создан канал и настроены визуализации и виджеты, мы можем перейти на вкладку API Keys (Ключи API), скопировать, а затем записать ключ API в наш файл secrets.py или просто сохранить его где-нибудь для дальнейшего использования. Эта информация понадобится вам на следующем этапе. Теперь мы готовы заняться кодом проекта.
466  Сервис ThingSpeak Рис. 15.14  Компоновка информационной панели проекта Plants IoT Необходимые компоненты Таблица 15.1 представляет собой копию таблицы из главы 10 и перечисляет компоненты, которые вам понадобятся в дополнение к плате MicroPython и USB-кабелю, за исключением RTC, поскольку ThingSpeak добавит время записи значения самостоятельно. Таблица 15.1. Необходимые компоненты Компонент Кол-во Описание Ссылка Датчик влагосодержания почвы 1+ Резистивный www.sparkfun.com/products/13637 датчик влажности для почвы Перемычки 3 (мин) Перемычки «штырь/штырь» www.sparkfun.com/products/8431 Теперь давайте посмотрим, как соединить компоненты вместе – еще раз: это скопировано из главы 10 для ясности.
Проект: IoT-мониторинг растений  467 Настройка оборудования Посмотрим, как подключить Raspberry Pi Pico и Arduino Nano RP2040 Connect. Как вы увидите, соединения аналогичны, но места подключения выводов модулей к платам различаются. Начнем с Pico. Соединения для Raspberry Pi Pico В табл. 15.2 показаны подключения, необходимые для этого проекта на основе Raspberry Pi Pico. Поскольку Pico имеет несколько контактов GND, мы будем использовать их для наших датчиков. Таблица 15.2. Соединения для проекта Plants IoT (Pico) Физический контакт (Pico) Название/номер GPIO (Pico) Вывод на внешней плате 27 GP20 Датчик 1 – VCC 32 GP27 Датчик 1 – SIG – Макетная плата (общий) Датчик 1 – GND 29 GP21 Датчик 2 – VCC 34 GP28 Датчик 2 – SIG – Макетная плата (общий) Датчик 2 – GND 28 GND Макетная плата (общий) На рис. 15.15 показана схема подключения для проекта Pico. Рис. 15.15  Подключение проекта Plants IoT (Pico)
468  Сервис ThingSpeak Соединения для Arduino Nano RP2040 Connect В табл. 15.3 показаны соединения, необходимые для Nano RP2040. Таблица 10.3. Соединения для проекта Plants IoT (Nano RP2040) Обозначение на плате Nano RP2040 (номер GPIO/функция) Соединение D8 (GP20) Датчик 1 – VCC A1 (GP27) Датчик 1 – SIG Макетная плата (общий) Датчик 1 – GND D9 (GP21) Датчик 2 – VCC A2 (GP28) Датчик 2 – SIG Макетная плата (общий) Датчик 2 – GND GND Макетная плата (общий) На рис. 15.16 показана схема подключения для проекта Nano RP2040. Рис. 15.16  Подключение проекта Plants IoT (Nano RP2040) Теперь давайте поговорим о коде, который нам нужно написать. Пока не включайте плату – необходимо немало обсуждений, прежде чем мы будем готовы протестировать проект.
Проект: IoT-мониторинг растений  469 Написание кода В этом разделе мы изменим проект из главы 10 для работы с ThingSpeak. Нам понадобится модуль thingspeak.py, который мы создали ранее, и файл sec­rets.py, который мы будем использовать для хранения наших SSID, SSID_PASS и ключа записи API ThingSpeak. Мы также будем использовать тот же файл read_timer. py1, который мы создали в главе 10. Обязательно установите разумную задержку. Файл secrets.py должен быть создан со следующим содержимым и сохранен в каталоге project7 на вашей плате MicroPython: # WiFi Parameters SSID = "MY_SSID" SSID_PASS = "MY_SSID_PASSWORD" # API KEY THINGSPEAK_WRITE_APIKEY = "MY_API_WRITE_KEY" Нам осталось обсудить только два файла кода: модификацию нашего класса SoilMoisture из главы 10 и новый основной файл кода. Давайте посмотрим на них. Класс SoilMoisture для IoT-проекта Вспомним главу 10: наш класс SoilMoisture был разработан для чтения датчиков и последующей записи данных в log-файл. Мы должны изменить код, чтобы он просто возвращал словарь значений с одной парой ключ–значение для каждого датчика. Мы отформатируем словарь так, чтобы его можно было легко отправить в ThingSpeak. Как и в предыдущих главах, мы переименуем файл кода в soil_moisture_iot.py и класс в SoilMoistureIoT. Однако мы не можем использовать код в том виде, в котором он написан. Мы должны удалить код для записи в log-файл журнала, а также функции даты и времени из модуля RTC. Нам нужен новый конструктор, приватная функция _get_values() (с некоторыми изменениями) и серьезные модификации функции read_values(). Давайте посмотрим на функцию read_values(), а остальные изменения оставим в качестве упражнения. Для функции read_values() из главы 10 требовался код для обработки запи­ си в log-файл, но теперь нам нужно просто прочитать данные датчиков и вернуть словарь. Словарь создан для записей вида fieldN: valueN, начиная с field1 для первого датчика и field2 для второго и т. д. Затем мы получаем значение, прочитанное датчиком, и сохраняем его как значение в словаре под этим ключом. Это нам значительно помогает, потому что теперь основному коду нужно только вызвать эту функцию, а затем отправить данные в ThingSpeak без дальнейших изменений. В лис­тин­ге 15.4 показан код модифицированной функции read_values(). 1 См. сноску на стр. 348. – Прим. перев.
470  Сервис ThingSpeak Листинг 15.4  Измененная функция read_values() # Monitor the sensors, read the values, and save them def read_sensors(self): values_read = {} # Create a new dictionary for the values for i in range(0,self.num_sensors): field_name = "field{0}".format(i+1) # Read the data from the sensor and convert the value sensor = self.sensors[i] value = self._get_value(sensor['sensor'], sensor['power']) values_read.update({field_name:value}) print("Value read: {0}".format(value)) return values_read Уделите некоторое время изучению кода, пока не убедитесь, что знаете, как он работает. В лис­тин­ге 15.5 показан готовый файл кода класса SoilMoistureIoT. Как вы заметите, в коде есть и другие изменения, и файл кода намного меньше того, который мы использовали в главе 10. Большая часть дополнительного кода была удалена для log-файла и получения даты и времени из RTC, что больше не требуется. Это показывает, насколько проще может быть код при работе с облачными системами. Отлично! Листинг 15.5  Класс SoilMoistureIoT # # MicroPython for the IoT Second Edition - Chapter 15 # # MicroPython IoT Plant Monitoring with ThingSpeak # # Soil Moisture class # import os import time from machine import ADC, Pin # Thresholds for the sensors LOWER_THRESHOLD = 500 UPPER_THRESHOLD = 2500 UPDATE_FREQ = 120 # seconds class SoilMoistureIoT: """ This class reads soil moisture from one or more sensors and writes the data to a comma-separated value (csv) file as specified in the constructor. It also requires a list (array) or sensor dictionaries in the following form. sensor = { 'pin': sensor_pin, 'power': power_pin, 'location': location or description
Проект: IoT-мониторинг растений  471 'nick': nickname for the sensor } Sensors are numbered in the order they appear in the list. Note: Intermediate calls to get_values() will return the last value(s) read or None if no sensors have been read. """ # Initialization for the class (the constructor) def __init__(self, sensor_list): # Loop through the sensors specified and setup a new dictionary # for each sensor that includes the power and ADC pins defined. self.sensors = [] for sensor in sensor_list: # Setup the dictionary for each soil moisture sensor soil_moisture = { 'sensor': ADC(Pin(sensor['pin'])), 'power': Pin(sensor['power'], Pin.OUT), 'location': sensor['location'], 'nick': sensor['nick'] } self.sensors.append(soil_moisture) self.num_sensors = len(self.sensors) print("Soil moisture sensors are ready...") # Read the sensor 10 times and average the values read def _get_value(self, adc, power): total = 0 # Turn power on power.high() # Wait for sensor to power on and settle time.sleep(5) for i in range (0,10): time.sleep(1) # Read the value value = adc.read_u16() total += value # Turn sensor off power.low() return int(total/10) # Monitor the sensors, read the values, and save them def read_sensors(self): values_read = {} # Create a new dictionary for the values for i in range(0,self.num_sensors): field_name = "field{0}".format(i+1) # Read the data from the sensor and convert the value sensor = self.sensors[i] value = self._get_value(sensor['sensor'], sensor['power']) values_read.update({field_name:value}) print("Value read: {0}".format(value)) return values_read
472  Сервис ThingSpeak Теперь давайте посмотрим на основной код. Основной код Файл основного кода этого проекта можно назвать plant_iot.py, и, как и класс SoilMoistureIoT, он намного проще за счет использования созданного нами ранее модуля thingspeak.py. Все, что нам нужно в основном коде, – это цикл, который использует класс ReadTimer для считывания датчиков в каждом интервале выборки и отправки данных в ThingSpeak. Поскольку наши секретные значения хранятся в файле secrets.py, нам не нужно засорять наш основной код кучей констант. Красиво и аккуратно. Вместо того чтобы предлагать подробное описание основного кода, мы представляем его целиком, оставляя разбор кода в качестве упражнения. В лис­тин­ге 15.6 показан завершенный файл основного кода. Как вы увидите, он очень похож на то, что мы видели в главе 10, только упрощен для использования с ThingSpeak, но он также представляет типичный код, который можно найти при создании аналогичных облачных IoT-решений «чтение– обновление–просмотр». Листинг 15.6  Основной код (plants_iot.py) # # MicroPython for the IoT Second Edition - Chapter 15 # # Chapter 15 - IoT Plant Monitoring with ThingSpeak # # Required Components: # - (N) Soil moisture sensors (one for each plant) # # Imports for the project import network, sys, time from project7.read_timer import ReadTimer from project7.soil_moisture_iot import SoilMoistureIoT from project7.thingspeak import ThingSpeak # Main function def main(): # Setup ThingSpeak and connect to the network thingspeak = ThingSpeak() if not thingspeak.connect(): sys.exit(-1) # Setup the sensors sensor_list = [ { # Arduino Nano RP2040 Connect and Pico uses pin=27, power=20 'pin': 27, 'power': 20, 'location': 'Green ceramic pot on top shelf', 'nick': 'Ivy',
Выполнение кода  473 }, { # Arduino Nano RP2040 Connect and Pico uses pin=28, power=21 'pin': 28, 'power': 21, 'location': 'Fern on bottom shelf', 'nick': 'Fern', } ] # Setup the soil moisture object instance from the SoilMoistureIoT class plants = SoilMoistureIoT(sensor_list) # Setup read event handler data_read_event = ReadTimer(120000) print("Ready for connections...") while True: if data_read_event.time_to_read(): data_read_event.reset() print("Reading data...") data = plants.read_sensors() # Send data to ThingSpeak channel thingspeak.upload_data(data) time.sleep(1) if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit) as err: print("\nbye!\n") sys.exit(0) Уделите некоторое время изучению этого кода, пока не поймете, как он работает. Прежде чем запустить проект, убедитесь, что в папку project7 на вашей плате MicroPython загружены файлы read_timer.py, secrets.py, thingspeak.py и soil_moisture_iot.py. Вот и все; мы готовы реализовать проект. Нужно дать ему поработать несколько минут, чтобы получить некоторые данные. Если вы запускаете проект в стабильной среде, где значения не меняются, вы можете не заметить особых изменений. В качестве упражнения рассмотрите возможность изменения окружающей среды, чтобы стимулировать изменения данных. Не используйте огонь и не прикасайтесь к электронике во время ее работы. Выполнение кода На этом этапе вы можете настроить оборудование и запустить проект. Дайте ему поработать около 20 минут, а затем посетите страницу своего канала ThingSpeak. Для получения лучших результатов в стабильной среде следует
474  Сервис ThingSpeak рассмотреть возможность изменения частоты выборки с 30 секунд до 4–6 часов. Это должно показать, как данные меняются в течение дня. Когда вы закончите с исправлениями и ThingSpeak обновит представление, вы должны увидеть четыре виджета, подобные показанным на рис. 15.17. Рис. 15.17  Примеры результатов с датчиками (Plants IoT) Обратите внимание, что теперь мы можем видеть изменения данных с течением времени, а также текущий статус влажности почвы для каждого растения. Отлично! Также обратите внимание на разницу в графике и шкале для растений. Здесь линейный график не раскрывает всей истории, но, глядя на шкалу, мы видим, что почва почти сухая и требует полива. Оставьте этот проект запущенным на несколько дней или попробуйте его с другими растениями. Вы обнаружите, что этот простой проект поможет вам следить за своими растениями, даже когда вас нет дома. И самое главное, это красивый и практичный проект, который вы можете показать своим друзьям и побудить их приобрести собственную плату MicroPython. ПРИМЕЧАНИЕ Напомним, что мы можем удалить данные канала, нажав Settings (Настройки) на домашней странице и Clear Channel (Очистить канал) внизу.
Итоги  475 А что дальше? Этот проект, как и предыдущий, показывает отличные перспективы для повторного использования методик в других проектах. Это особенно актуально теперь, когда вы знаете, как использовать MQTT. Если вам нравится просмат­ ривать данные своих датчиков через интернет, вам следует подумать о том, чтобы потратить время на изучение некоторых дополнений. Вот некоторые из них, которые вы можете рассмотреть. Иные могут оказаться сложной задачей и потребовать дополнительных исследований:   добавьте больше датчиков, чтобы расширить свой проект для большего количества растений;   добавьте датчик температуры для регистрации температуры окружающей среды и отображения ее на датчике;   добавьте светодиоды на свою плату и информационную панель, чтобы они светились, когда растения нуждаются в поливе;   добавьте кнопку на информационную панель для принудительного считывания данных с датчиков, чтобы можно было считывать влажность почвы по запросу, а не только пассивно. Уделите некоторое время изучению этих потенциальных дополнений – это будет хорошей практикой. Итоги Взяв небольшую плату, такую как Pico или Nano RP2040 Connect, добавив датчики и написав относительно короткий код MicroPython, мы получаем возможность просмотреть данные из любой точки мира. Мы узнали, как можно использовать новые знания о MicroPython и платах контроллеров для создания настоящих IoT-решений. В этой главе мы продемонстрировали концепции интернета вещей в рабочем проекте, создав проект мониторинга растений, с подключением платы MicroPython к облачному сервису ThingSpeak и используя этот сервис для хранения и визуализации данных. Хотя этот проект кажется более простым, чем предыдущие, в последних мы получили специальные знания, которые облегчили реализацию проекта. Теперь мы можем создавать проекты интернета вещей для отправки данных в облако и обмена ими со всем миром. Мало того, мы также можем создавать решения, которые позволяют нам управлять нашими проектами из интернета, – все делается быстро, с минимальными усилиями и без необходимости изучать сложный API.
476  Сервис ThingSpeak Благодаря опыту, полученному в этой книге, вы теперь готовы создавать полноценные IoT-решения: в диапазоне от простых проектов, которые вы запускаете для развлечения, до законченных облачных решений. А сейчас пришло время включить свое воображение и использовать инструменты и методы, которые вы изучили в этой книге, для создания собственных IoTре­шений MicroPython. В следующей главе мы увидим, как вы можете использовать то, что узнали из этой книги, в дальнейшем, планируя новые проекты MicroPython и присоединяясь к растущему сообществу любителей и энтузиастов MicroPython.
Глава 16 Куда двигаться дальше? Теперь, когда вы получили подробное представление о MicroPython, платах модулей и разнообразном электронном оборудовании, интернете вещей, типах проектов, которые вы можете создавать, а также об учебных пособиях и примерах, пришло время подумать о том, что вы можете делать за пределами этой книги. В данной главе мы рассмотрим, что вы можете сделать, чтобы совершенствовать мастерство создания решений интернета вещей. Большинство людей захотят просто продолжать разрабатывать проекты для себя лично, либо ради развлечения, либо для решения проблем дома или в офисе. Однако некоторые захотят поднять свои навыки на новый уровень. В любом случае есть несколько вещей, которые вам следует учитывать. В следующих разделах мы рассмотрим источники дополнительных примеров проектов, способы присоединения к сообществу энтузиастов интернета вещей через социальные сети и другие интернет-ресурсы и, наконец, как стать активным членом растущего сообщества разработчиков. Новые проекты для изучения Если вы хотите поработать над другими IoT-проектами MicroPython, вы будете рады узнать, что существует множество примеров, которые вы можете изучить. Большинство примеров находятся либо на различных сайтах с документацией, либо являются вкладами сообщества: от общего обзора до подробных инструкций по завершению проекта. К сожалению, большинство примеров представлены практически без таких инструкций. Однако теперь, когда у вас есть подробные инструкции1 по работе с IoT-проектами MicroPython и различными платами MicroPython, вы сможете выполнять примеры практически без подробных инструкций. Существует несколько репозиториев примеров IoT-проектов MicroPython. Большинство из них находятся на форумах, посвященных MicroPython или 1 Это одна из моих главных мотиваций при написании данной книги. – Прим. авт.
478  Куда двигаться дальше? платам MicroPython (например, Raspberry Pi Pico), но есть еще классный сайт Hackster.io, который представляет собой общий форум сообщества аппаратного обеспечения (www.hackster.io). Чуть позже мы увидим, как ориентироваться на этом сайте. Для начала давайте посмотрим на пару ресурсов с примерами проектов MicroPython. Примеры проектов MicroPython Есть несколько веб-сайтов, на которых размещены примеры MicroPython. Существует три основных типа таких веб-ресурсов: форумы, на которых имеются разделы, предназначенные для публикации своих проектов, сайты документации, на которых есть примеры проектов, и репозитории, куда люди могут загружать свои проекты. Давайте рассмотрим каждый из них. СОВЕТ Не пренебрегайте традиционным способом найти примеры MicroPython – ввести в Google «MicroPython Example» («Пример MicroPython»). Вы получите множество результатов, включая ресурсы, перечисленные в этой главе. Форумы Форумы, представляющие первый тип, предлагают список категорий, начиная от объявлений, вопросов и ответов, часто задаваемых вопросов до технической поддержки и многого другого. В том числе у некоторых из них также есть категория проектов. Иногда категория включает в себя множество записей (тем), большинство из которых представляют собой вопросы о том, как реализовать определенный проект. На форуме MicroPython есть такая категория (https://forum.micropython.org/viewforum.php?f=5). И хотя здесь более ста тем и большинство из них – вопросы, здесь есть и несколько жемчужин, которые вы можете изучить. Использование форума для поиска примеров проектов или образцов может потребовать некоторой работы, поскольку они предназначены для использования сообществом и, следовательно, содержат много вопросов и комментариев, но я считаю, что они по-прежнему остаются одним из лучших ресурсов. Имейте в виду, что они, как правило, касаются конкретной платы. Лучший способ их использовать – перейти на форум и либо пролистать темы, либо выполнить поиск по ключевой фразе. Документация Документация по MicroPython также является хорошим ресурсом для поиска примеров кода (однако, как правило, не целых проектов). Обычно они лучше, чем форумы, потому что гораздо лучше организованы и иногда более гра-
Новые проекты для изучения  479 мотно написаны. Как и форумы, сайты документации MicroPython, особенно примеры, ориентированы на конкретное аппаратное обеспечение. Однако в документации можно встретить и несколько примеров проектов. Даже если, как некоторые из них, проекты документированы частично, код в этих примерах, как правило, в целом намного превосходит по качеству, который вы можете найти на форумах. Лучший способ использовать сайты документации – просто перейти на них и просмотреть оглавление (это само по себе делает их удобнее форумов). Например, на рис. 16.1 показан пример проекта из документации Arduino Nano RP2040 Connect для использования с помощью MicroPython функции BLE (https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-pythonapi#bluetooth-low-energy). Рис. 16.1  Пример проекта BLE MicroPython (с разрешения arduino.cc) Репозитории Лучшие веб-сайты для проектов MicroPython – это репозитории. Обычно они размещаются в службе контроля версий, например GitHub. Эти сайты наиболее полезны потому, что вы можете перейти непосредственно к исходным файлам и просмотреть код в своем браузере, пропуская страницу документации или демонстрацию. Это удобно, если вы просто хотите посмотреть, как реализовать какую-то функцию, а не просматривать длинную страницу текста. Конечно, лучший способ использовать образцы – загрузить весь набор образцов. Просто посетите главный сайт GitHub и загрузите примеры проектов. Как это, круто?
480  Куда двигаться дальше? Основной сайт MicroPython (https://github.com/micropython/micropython) – один из лучших репозиториев проектов MicroPython. В этом репозитории содержится множество примеров, и хотя некоторые из них ориентированы на конкретную плату MicroPython, вы можете использовать их в качестве шаб­лонов для написания кода для самых разных проектов. Просто обязательно выясните, совместимо ли используемое оборудование с вашей платой. На рис. 16.2 показан фрагмент страницы примера репозитория. Рис. 16.2  Примеры проектов в репозитории MicroPython GitHub Большинство (я не проверял абсолютно все!) образцов размещаются по лицензии с открытым исходным кодом, такой как лицензия MIT. Что очень удобно для всех, поскольку лицензия MIT позволяет использовать и даже заново публиковать код. (Образец лицензии MIT см. на https://opensource.org/ licenses/MIT.) Это здорово, поскольку я много раз хотел использовать образец проекта в демонстрационных целях, но обнаруживал, что лицензия не позволяет такое использование. Сайты проектов сообщества: Hackster.io Сайт Hackster – это сообщество, посвященное изучению аппаратного обес­ печения. Вы можете найти всевозможные примеры проектов аппаратного обеспечения, в том числе многие из них для MicroPython, Python, Raspberry Pi, Arduino и других! Существует небольшой, но растущий список проектов MicroPython. Посетив главную страницу сайта (www.hackster.io), вы можете искать проекты, введя «MicroPython» в поле поиска. После ввода критериев
Новые проекты для изучения  481 поиска на странице результатов поиска вы увидите все проекты, которые можете изучать. Каждый из них отмечен относительным уровнем сложности: от простого до продвинутого. Вы также увидите количество просмотров примера проекта и количество оценок, полученных проектом от других участников сообщества (вы должны присоединиться к Hackster.io, чтобы могли оценить проект). Самое приятное то, что есть раздел комментариев, который вы можете использовать, чтобы подбодрить дизайнера или попросить его о помощи с проектом. СОВЕТ Используйте золотое правило при публикации комментариев или вопросов на онлайн-форумах. Не поддавайтесь искушению опубликовать свое мнение, разжечь пламя флуда или насмешек и придерживайтесь только фактов. Что мне больше всего нравится на сайте Hackster, так это то, что образцы, как правило, хорошо документированы и часто включают фотографии. Благодаря уникальной структуре сайта образцы разбиты на части, что облегчает их использование. Например, веб-страница «Символьный ЖК-дисплей через I2C» (www.hackster.io/dzerycz/character-lcd-over-i2c-ba8ee9) содержит обзорный раздел, в котором представлены краткое описание проекта, связанные теги, рейтинг сложности, дата публикации и даже лицензия. Это значительно упрощает рассмотрение проекта. Например, есть отличный пример проекта среднего уровня, который не только хорошо документирован, но и хорошо написан. Это «Детектор утечек MicroPython с Adafruit и Home Assistant» Робина Коула (Robin Cole). На рис. 16.3 показан отрывок из обзора проекта. Если вы хотите увидеть еще один отличный проект, посетите www.hackster.io/robin-cole/micropython-leakdetector-with-adafruit-and-home-assistant-a2fa9e. Если вы прокрутите обзор вниз, вы найдете разделы, демонстрирующие, как подключить оборудование (так, как я представляю проекты в этой книге), короткую прогулку по коду, а также описания и демонстрации того, как использовать проект. Некоторые примеры включают короткие видеоролики, демонстрирующие или объясняющие проект. В конце страницы вы найдете раздел комментариев, который можете использовать, чтобы прочитать, что другие сказали о проекте, а также вопросы, которые у других возникли относительно проекта. Если вы застряли на примере, обязательно прочитайте все комментарии – велика вероятность, что кто-то уже задавал ваш вопрос или решал аналогичную проблему. А что насчет hackaday.io? Еще один сайт, похожий на Hackster.io, который набирает популярность, – это Hackaday.io. Он очень похож на сайт Hackster.io тем, что вы можете искать проекты MicroPython. Мне он не нравится так сильно, как Hackster.io, но не позволяйте этому обстоятельству помешать вам его изучить – пробуйте!
482  Куда двигаться дальше? Рис. 16.3  Пример проекта на Hackster.io Хранилище знаний learn.adafruit.com Еще один отличный источник информации о MicroPython – learn.adafruit. com. Этот сайт содержит статьи, блоги и учебные пособия по широкому кругу тем. Как и на форумах пользователей, на этот сайт часто добавляется контент. Следует периодически посещать сайт и искать свою тему. Например, чтобы найти сведения о MicroPython, просто ищите «MicroPython». Или можно использовать эту ссылку: https://learn.adafruit.com/ search?q=micropython. На рис. 16.4 показан отрывок с сайта learn.adafruit.com, показывающий интересный контент о MicroPython. Обязательно проверьте сайт на предмет последних дополнений.
Новые проекты для изучения  483 Рис. 16.4  Содержимое MicroPython на сайте learn.adafruit.com Обратите внимание, что вы можете изучить множество различных категорий. Просмотрите эти категории, чтобы увидеть широкий охват, который предлагает данный сайт. Я часто использую этот ресурс в поиске идей для проектов. Даже если статья, блог или руководство не совсем то, что вам нужно, или не соответствуют вашему оборудованию, часто стоит прочитать их, чтобы получить советы. Например, если вы планируете создать проект с использованием светодиода Charlieplex (www.adafruit.com/?q=charlie), вы можете найти руководство по адресу https://learn.adafruit.com/micropythonhardware-charlieplex-led-matrix/software?view=all. Хотя в этом руководстве рассматриваются платы Feather и ESP8266, вы все равно можете узнать немало об использовании светодиода Charlieplex, включая драйвер (который вы можете откорректировать в своих целях), подсказки и сведения о том, как заставить этот светодиод работать с другими платами MicroPython.
484  Куда двигаться дальше? Теперь, когда вы ознакомились с парой ресурсов с примерами проектов, давайте обсудим, как вы можете присоединиться к сообществу и внести свой вклад в хранилище того, что связано с MicroPython для IoT. Присоединяйтесь к сообществу После того как вы освоили примеры проектов из этой книги, а также некоторые из других ресурсов, пришло время пойти дальше в своем хобби и присоединиться к сообществу разработчиков и энтузиастов интернета вещей. В этом разделе я обсуждаю некоторые причины, по которым вы можете захотеть поделиться своими знаниями, этикет обмена и внесения вклада, а также несколько примеров сообществ, к которым вы, возможно, захотите присоединиться или, по крайней мере, следить за ними. Как вы увидите, большинство из них не посвящены MicroPython, но могут служить отличным источником идей. Начнем с причин, почему мы можем захотеть поделиться. Зачем вносить вклад? По мере того как все больше и больше энтузиастов увлекаются такими хобби, как MicroPython и IoT, все более распространенной становится концепция совместного использования. Это не случайно. Многие из основателей и пио­ неров Python, MicroPython и IoT являются сторонниками открытого оборудования и открытого исходного кода. Это относится не только к аппаратному и программному обеспечению, но также и к другим интеллектуальным продуктам, таким как исходный код и документация для опубликованных проектов. Многие считают, что их код должен быть бесплатным, чтобы каждый мог его использовать и модифицировать со взаимными ожиданиями. Например, если вы изменяете чужой дизайн или код, вы должны поделиться не только улучшенным дизайном, но и указать автора оригинала. Это может означать предоставление первоначальному автору уже ваших изменений. Пока вы следуете правилам лицензии, обмен информацией осуществляется честно и хорошо. Однако в зависимости от того, как был лицензирован пример кода, могут быть некоторые ограничения на то, чем можно делиться. Например, может оказаться невозможным совместно использовать код из проприетарной биб­ лиотеки. Хотя вы можете быть создателем кода, использующего библиотеку, вы не являетесь владельцем библиотеки и не можете делиться ею. Скорее всего, вы сможете поделиться своим кодом с другими, но публикация может быть ограничена. Совместное использование проектов также означает размещение их там, где другие смогут их найти. Возможно, вы захотите сделать их бесплатными
Присоединяйтесь к сообществу  485 для всех или ограничить возможности людей что-то делать с вашим проектом. Есть веб-сайты, которые неплохо с этим справляются. Итак, зачем вносить вклад в чужой проект? Есть много причин, включая тот факт, что автору может быть приятно видеть, как один из его проектов используется и дополняется кем-то другим. Возможно, самая важная причина внести свой вклад – помочь другим узнать, что у вас есть решение задачи и что вы можете научить избегать ловушек и проблем. Таким образом, мы все выиграем, изучая лучшие практики или просто лучшие способы реализации наших идей. Наконец, ваш собственный проект и опыт, если вы им поделитесь, вдохновят других на создание других проектов или, возможно, на улучшение вашего. У меня такое случалось с моими собственными проектами. Люди взяли то, что я сделал, и улучшили. Поскольку они, в свою очередь, поделились своим проектом, я смог включить множество их улучшений в свои проекты, сделав их даже лучше, чем предполагал вначале. Какая лицензия, где ее узнать? Итак, как узнать, какая лицензия используется? Каждый веб-сайт, на котором размещен исходный код, документация, примеры и т. д. в любой форме, будет иметь четко обозначенную лицензию. Она может быть указана на экране внизу главной страницы, или в другом отдельном месте, или даже только на специальной странице с заголовком «лицензия» или аналогичной меткой. Например, лицензия на веб-сайте micropython.org расположена под заголовком «Completely free, open source software» (Полностью бесплатное программное обеспечение с открытым исходным кодом), ссылается на лицензию MIT для ядра MicroPython и кратко формулирует эту лицензию следующим образом: «You can freely use and adapt MicroPython for personal use, in education, and in commercial products» (Вы можете свободно использовать и адаптировать MicroPython для личного использования, в образовательных целях и в коммерческих продуктах). Аналогично на сайте github.com есть раздел для указания лицензии в каждом случае, когда кто-то загружает проект. На рис. 16.5 показан пример типичной лицензии GNU Public License версии 3 (GPLv3), которую можно найти по адресу https://opensource.org/license/gpl-3-0/. Большинство проектов предоставляют либо файл, содержащий лицензию, либо ссылку, по которой можно щелкнуть, чтобы прочитать лицензию. К счастью, большинство проектов на Hackster.io и других сайтах имеют открытый исходный код, и вы можете смело использовать их в своих проектах (но все равно проверяйте!). СОВЕТ Всегда проверяйте лицензию любого используемого вами примера перед его публикацией, чтобы убедиться, что вы соответствуете не только пожеланиям автора, но и юридическим ограничениям назначенной лицензии.
486  Куда двигаться дальше? Рис. 16.5  Пример лицензии: GPLv3 Теперь давайте сосредоточимся на том, как делиться своими проектами. Как делиться Вы можете быть удивлены, почему кто-то хочет отдать бесплатно то, над чем он работал часами. Хотя правда, что вы должны делиться своими крутыми проектами с другими, это не жесткое правило. Есть многие, кто сделал свои проекты доступными за определенную плату аналогично продаже IoT-решения на коммерческой основе. Однако подавляющее большинство энтузиастов делятся своими идеями и проектами бесплатно. Существует несколько сообществ, где вы можете поделиться своими проектами, и некоторые из них мы увидим в следующем разделе. Но сначала нужно кое-что понять о совместном использовании проектов. Существует набор правил (некоторые прописаны явно, некоторые нет), которым вы должны следовать, если решите присоединиться к сообществу интернета вещей или любому другому подобному. Ниже перечислены некоторые рекомендации и правила, которые вам следует учитывать, делясь своими идеями, проектами и комментариями с сообществом. Представляйте свой уникальный дизайн Никто не любит подражателей. Вам это не нравилось, когда вам было пять лет, и вы не оцените это, когда увидите, что что-то, что вы разработали и чем поделились бесплатно, было представлено как «дизайн месяца», приписываемый кому-то другому. Таким образом, вы должны хорошо поработать, чтобы убедиться, что ваш дизайн уникален. Не обязательно целенаправленно переделывать свой ди-
Присоединяйтесь к сообществу  487 зайн, чтобы он не был похож на чужой, но стоит проявить должную осмотрительность и хотя бы поискать похожие проекты. Помните: это нормально, если вы разрабатываете аналогичный проект, но обычно это плохой тон (или, возможно, нарушение лицензии) – просто копировать то, что опубликовал кто-то другой. В том редком случае, когда случается, что ваш проект почти идентичен другому, то, пока ваша работа принадлежит вам, проблем быть не должно. На самом деле это случилось со мной однажды. Комментарии другого дизайнера и мои были примерно такими: «Классный проект. Здорово придумано, да?» Опять же, в этом нет ничего плохого, при условии что вы оба признаете сходство и нет проблем с лицензированием. Если другой проект, подобный вашему, действительно имеет тот же дизайн, но был лицензирован по-другому, возможно, вам придется договориться с другим дизайнером. Это может произойти, когда проекты имеют лицензию на владение (например, коммерческая собственность), но подобное случается редко, поскольку большинство репозиториев образцов интернета вещей представляют собой сайты, на которых люди бесплатно делятся своими проектами. Давайте посмотрим на другой пример, не связанный с исходным кодом. Какова вероятность того, что десяток разных корпусов для Raspberry Pi будут одинаковыми по размеру, иметь одинаковые отверстия для портов и, возможно, даже собираться одинаково (например, защелкиваться)? Очень вероятно, да? Под уникальностью я подразумеваю то, что среди этих 10 случаев должны быть явные различия между ними – то, как они печатаются (например, ориентация на рабочей платформе), состоят ли они из нескольких частей, были ли они разработаны с учетом вентиляции и т. д. Даже если бы все 10 проектировщиков начали работу одновременно по одному заданию, были бы некоторые различия. У каждого – своя работа. То есть никто не использовал дизайн другого, чтобы выдать его за свой. В случае программного проекта исходный код, скорее всего, будет немного отличаться от примеров. Юристы должны разобраться в том, насколько велика разница, позволяющая считать исходный код отличающимся. Достаточно сказать, что если ваш код и код другого почти идентичны, но созданы без знания другого, можно поделиться своим кодом, при условии что нет лицензионных конфликтов1. 1 Автор в этом разделе по сути пытается своими словами вникнуть в разницу между авторским и патентным правом. Авторский продукт (текст, картина, дизайн корпуса) – то, что не может быть повторено даже случайно при одинаковой конечной задаче; продукты разных авторов всегда будут иметь различия. Обычно проблем здесь не возникает – если вы видите идентичные авторские продукты, значит, наверняка имеет место копирование кого-то одного из них у другого. Патентное право имеет дело с решениями, которые, наоборот, при тех же исходных позициях и конечных целях повторят одно другое почти наверняка: так, если вы зададитесь целью измерять температуру по изменению сопротивления образца, то со 100%-ной вероятностью придете к одной их схем, уже придуманных до вас за последние полтора
488  Куда двигаться дальше? Наконец, когда вы публикуете свой проект, основанный на чужой работе, вы должны аннотировать код, документацию и веб-сайт оригинального проекта, отдавая должное оригинальному дизайнеру. То есть вы однозначно заявляете, что ваш проект является производным от оригинала. Также хорошим тоном будет включить ссылку на оригинальный дизайн вместе со списком ваших модификаций. Опять же, предполагается, что лицензия разрешает это. Проверка лицензии Я упомянул лицензирование в контексте загрузки и использования примеров проектов. Напомним, что большинство репозиториев потребуют от вас указать лицензию для вашего проекта. Это позволяет репозиторию размещать ваш проект и сообщать всем о ваших намерениях относительно владения, разрешений на использование и т. д. Как я уже говорил ранее, вам необходимо проверить лицензию перед использованием любого дизайна. Если вы планируете его модифицировать, вам необходимо обратить пристальное внимание на лицензию. Подавляющее большинство лицензий позволит вам использовать проект, а большинство – изменять его. Различия в некоторых лицензиях касаются права собственности на модификации. Некоторые лицензии с открытым исходным кодом, такие как GPL, допускают внесение изменений, но требуют от вас передать эти изменения первоначальному владельцу (человеку или организации, создавшей и лицензировавшей их), если вы планируете их распространять. То есть вы можете изменить код по своему желанию для личного использования, но как только вы опубликуете эти изменения, вы должны передать их владельцу лицензии. Я сталкивался с этим всего пару раз, но в тех случаях дизайнер создавал прототипы для коммерческого продукта. В лицензии и тексте проекта было ясно, что им нужна помощь с дизайном, но дизайн не будет обнародован. Обратите на это внимание и действуйте осторожно. Любая работа, которую вы выполняете, может быть направлена на благо владельца, а не на утверждение ваших прав или получение прибыли. века. Понятно, что смысл делиться своими придумками (и ставить их под какую-то лицензию) имеет только в первом случае авторских продуктов; во втором случае решение либо окажется общеизвестным (и восходящим к одной из общих идей, изложенных в учебниках и пособиях, а как известно, «идеи не патентуются»), либо уже кем-то разработано (обычно производителем модуля или платы) и помещено под ту или иную лицензию. Пограничный и сложный случай представляют программы и электрические схемы (в общем случае, кроме тривиальных применений, их скорее можно причислять к авторским продуктам); но в любом случае необходимо убедиться в отсутствии чужого лицензирования, прежде чем публиковать под своим именем, как и излагает автор в следующем разделе. – Прим. перев.
Присоединяйтесь к сообществу  489 СОВЕТ Если у вас есть сомнения по поводу лицензии, обратитесь к создателю и спросите его напрямую. Поскольку большинство примеров проектов интернета вещей имеют лицензию на совместное использование и бесплатную модификацию, вам обычно не о чем беспокоиться. Тем не менее я рекомендую вам проверить лицензию перед использованием любого проекта, особенно если вы собирае­ тесь поделиться или опубликовать свой результат. Делайте уместные вещи Хотите верьте, хотите нет, но есть любители и энтузиасты с впечатляюще ярким воображением, придумавшие проекты интернета вещей, которые можно посчитать неуместными или даже непристойными. Независимо от ваших собственных взглядов, вы должны стремиться терпимо относиться ко взглядам других. Это не означает, что вы должны пойти на компромисс со своими взглядами; просто знайте, что ваша деятельность может оскорбить, и постарайтесь свести возможную обиду к минимуму. В частности, не загружайте проекты с неподходящей тематикой на сайты, доступные для просмотра всем. Может быть нормально загрузить какойнибудь проект, пропагандирующий некую тему, идеал и т. д.; только не загружайте проекты или комментарии, которые явно оскорбительны или направлены на причинение вреда. Например, если учесть тот факт, что IoT-проекты используются в школах для обучения детей технологиям, приемам работы с оборудованием и способам проектирования программного обеспечения, не следует загружать проекты с темами, которые родители могут счесть неуместными. Наиболее очевидными, конечно, являются оскорбительные выражения, темы для взрослых и клеветнические изображения. Вам следует проверить условия использования и пользовательское соглашение для сайта, на котором размещен выбранный вами репозиторий, в рамках принятия решения о запрете публикации. Обязательно прочтите раздел о том, что является уместным, а что нет, и придерживайтесь его. Есть еще один аспект, который следует рассмотреть. Вам нужно избегать загрузки примеров проектов, которые являются или потенциально могут быть незаконными или противоправными. Это может быть трудно установить, учитывая, что сообщество интернета вещей охватывает весь земной шар. Однако на большинстве сайтов есть формулировки, указывающие, что разрешено, а что запрещено. А у некоторых в соглашении есть формулировка, дающая сайту право удалять вещи, которые они считают неприемлемыми. Например, однажды я видел проект считывателя метки радиочастотной идентификации (RFID), который можно было бы использовать для считывания RFID на расстоянии. Это звучит безобидно, но учтите количество вещей, которые используют RFID, таких как ключи в отелях, удостоверения личности
490  Куда двигаться дальше? и даже кредитные карты. Очевидно, что считывание RFID с принадлежащих вам вещей – это нормально (действительно, это и продемонстрировал проект), но проект может быть (и, скорее всего, был) использован во зло. К счастью, это заметили другие, и сайт проекта был удален (URL выдает ошибку 401). Поэтому, прежде чем загружать дизайн или образец проекта, убедитесь, что вы понимаете и согласны с условиями пользовательского соглашения относительно того, что является подходящим, а что нет. В большинстве случаев недопонимание не приводит к неприятностям, но если вы сделаете это более одного раза, есть вероятность, что кто-то на сайте захочет переговорить с вами и ограничить ваш доступ, что возвращает меня к началу этого раздела – обязательно уважайте мнение других и особенно целевой аудитории сайта. Если вы не согласны, найдите другой сайт. Аннотируйте свою работу Один из способов определить, является ли образец проекта интернета вещей хорошим или качественным, – это то, как он аннотирован и документирован, то есть насколько хорошо дизайнер описал проект на своем сайте. Если я сталкиваюсь с проектом, который выглядит привлекательным, и обнаруживаю, что дизайнер не удосужился описать, как подключить оборудование, или объяснить код более чем семью словами, не предоставил никаких инструкций или не предоставил какие-либо фотографий фактической реализации, я не буду его использовать. Таким образом, вы должны стремиться представить как можно более полное описание. Вам не обязательно писать роман, повесть или диссертацию, но вы должны предоставить достаточно информации, чтобы описать предполагаемое использование, какую проблему проект решает, а также набор инструкций о том, как написать исходный код, скомпилировать и развернуть его. Единственным исключением является случай, когда вы все еще работаете над проектом или планируете внести изменения до его завершения. В этом случае вам следует пометить (аннотировать) проект, указав некоторые слова о незавершенной работе, экспериментальном статусе и т. д. Если в используемом вами репозитории есть функция, позволяющая пометить проект как неоконченный, используйте ее. Таким образом, другие будут знать, что ваш проект не совсем готов к всеобщему внедрению. Одной из причин для этого может быть получение обратной связи от других. Я делал это сам со смешанными результатами. В основном люди с радостью отмечают, что им это нравится, но не комментируют, а если и делают это, то не предлагают никаких изменений или улучшений. Я бы также посоветовал вам предоставить некоторую контактную информацию, чтобы другие, у кого есть вопросы, могли связаться с вами. Большинство сайтов позволяют зрителям легко связаться с вами через сайт, но вы можете предоставить иные формы связи (например, электронную почту).
Присоединяйтесь к сообществу  491 Возможно, вы не захотите указывать свой домашний адрес и номер телефона (и делать этого не стоит), но адрес электронной почты – хороший способ открыться сообществу. Например, я видел блоги, примеры проектов и даже учебные пособия, где люди размещали свой IRC-адрес1, адрес электронной почты и даже в одном случае свой рабочий номер телефона. Хотя я, возможно, не захожу так далеко, я предлагаю предоставить адрес электронной почты, чтобы вы могли общаться с людьми, которым нравится проект. Кроме того, приятно пообщаться с кем-то один на один и обсудить свою работу! Будьте хорошим гражданином Предположим, вы столкнулись с примером проекта, который не только некачественный, но и (по вашему мнению) неправильно спроектирован или реализован. Следует ли вам немедленно прокомментировать и сокрушить эго дизайнера легкомысленным замечанием о том, насколько глуп его код? Нет, конечно нет! Я бы, скорее всего, вообще проигнорировал такой проект. Я имею в виду, зачем усугублять ситуацию, указывая на недостатки? Я обнаружил, что сообщество в целом, скорее всего, сделает то же самое и не будет комментировать. Напомним, что одним из ключей к определению того, хорошо ли проект спроектирован (хорош), является то, сколько людей его используют. Обычно для этого есть счетчик. Если конструкция никому не понравилась и проект даже не скачивали, можете быть уверены, что он не попадет в топ ни одного поиска или примера проекта месяца. С другой стороны, если вы чувствуете необходимость прокомментировать, обязательно свяжитесь с дизайнером лично или будьте максимально конструктивны. Целью должно быть помочь дизайнеру улучшить свои проекты, а не бросать вызов его интеллекту или гордости. Когда я комментирую проекты, которые считаю странными и, возможно, ошибочными (а это случается редко), я обычно формулирую свои комментарии в форме вопроса. Вопрос не заставляет кого-то защищаться, и если он сформулирован правильно, он также не должен оскорблять. Например, я могу спросить: «Вы знаете, что код может зависнуть, если пользователь нажмет кнопку более одного раза?» Это хороший способ узнать, тестировал ли дизайнер свой проект в тех условиях, в которых вы ожидаете, что он потерпит неудачу. Это хорошая, конструктивная критика в самой интеллектуальной форме. Я уверен, что если вы подумаете о том, что собираетесь сказать, то сможете найти другие, возможно, более элегантные способы помочь людям улучшить их проекты. Теперь, когда мы поняли, почему мы делимся информацией и как делиться ею ответственно, давайте познакомимся с некоторыми сообществами, к которым вы, возможно, захотите присоединиться или хотя бы следить за ними. 1 IRC – первый в истории месенджер, популярный в 90-е годы. – Прим. перев.
492  Куда двигаться дальше? Рекомендуемые сообщества Существует довольно много общих веб-сайтов и онлайн-сообществ, посвященных Python и MicroPython, которые вы можете посетить и даже присоединиться. В большинстве онлайн-сообществ есть репозитории, в которых можно искать примеры, советы, методы и готовые проекты, которые можно изучить. Большинство из них также имеют одну или несколько областей, где участники могут комментировать, задавать вопросы или вообще общаться с другими участниками форума. Обычно вам необходимо присоединиться, чтобы иметь возможность опубликовать ответ или задать вопрос, но просмотр чаще всего разрешен всем. Лучший способ использовать такие ресурсы – периодически посещать их. В частности, вам следует регулярно читать статьи (которые вас интересуют) и сообщения на форуме. Это позволяет вам быть в курсе текущих событий, новых методов и даже новых решений сложных проблем. Например, я пытаюсь подключиться к сайтам Raspberry Pi, чтобы следить за тем, что там происходит. Я часто могу получить идеи для IoT-проектов или просто идеи для функций, просматривая проекты, реализованные для Raspberry Pi и его собственной операционной системы. Я считаю, что нередко бывает так, что хотя исходный код может сильно отличаться, большая часть оборудования применяется без изменений, что имеет смысл, поскольку оборудование не привязано к операционной системе, работающей на устройстве (это делают библиотеки, которые управляют оборудованием). Кроме того, не сбрасывайте со счетов возможности поиска по ключевым словам с помощью вашего любимого сервиса онлайн-поиска. Я часто находил малоизвестные жемчужины информации, которые не размещаются на более популярных сайтах. В большинстве случаев это хорошо написанные блоги. Я нередко начинаю с поиска по ключевым словам, прежде чем посетить сайты, перечисленные ранее. По крайней мере это подтверждает, является ли то, что я исследую, уникальным или повсеместным. Вам также следует рассмот­ реть возможность использования ключевых фраз из сообщений об ошибках в качестве условий поиска, чтобы получить помощь по конкретным ошибкам. Есть несколько отличных периодических изданий, которые вам следует приобрести. Я исключаю типичные периодические издания по Windows, ПК и общему программированию не потому, что они бесполезны, а потому, что они слишком общие для исследований, связанных с интернетом вещей. Ниже приведены периодические издания, которые, по моему мнению, очень полезны в моих исследованиях в области интернета вещей, а также в любом проекте в области электроники или хобби1. 1 Из русскоязычных ресурсов можно рекомендовать популярное сообщество Habr (https://habr.com/). Сайт посвящен широкой тематике, связанной с IT и программированием, и там в том числе регулярно появляются статьи о платформах и проектах, связанных с любительской электроникой, с MicroPython и интернетом вещей. Функционал сайта позволяет отфильтровать появление статей интересной вам тематики. – Прим. перев.
Стать мейкером  493   Журнал MagPi (raspberrypi.org/magpi): ежемесячный журнал, посвященный всему, что касается Raspberry Pi. Он включает в себя множество статей о примерах проектов, новости о Raspberry Pi, периферийных устройствах и общие обзоры оборудования.   Make (http://makezine.com/): журнал, посвященный широкому сооб­ щест­ву производителей, в котором представлены образцы проектов, учебные пособия, оборудование, инструменты, дроны, роботы и многое другое! Это действительно универсальное периодическое издание для всех, кто занимается хакерством, настройкой, а также для любителей, энтузиастов и профессионалов DIY. Теперь, когда мы рассмотрели несколько интернет-сообществ (но не полный список, так как новые добавляются, по-видимому, еженедельно), а также пару периодических изданий, которые вы можете приобрести, давайте обсудим следующий шаг, который можно предпринять, когда вы станете продуктивным участником IoT-сообществ и связанных с ними интернет-сообществ – то есть мейкером. Стать мейкером Следующий этап вашего роста от новичка до энтузиаста (и даже профессионала) – это регулярная практика своего ремесла и обмен знаниями с миром. Отличный выход для этого желания – стать создателем, мейкером (Maker). Мейкеры считаются экспертами в своих областях интересов. Самое приятное то, что интересы мейкеров могут сильно различаться. То есть стать мейкером – это не изучение определенного набора техник (академических или каких-либо других). Речь идет о занятиях ремеслом. Что такое мейкер? К сожалению, не существует единого определения того, кем является или должен быть мейкер. Мейкер – это человек с творческими способностями, который хочет работать над проектами, которые могут варьироваться от механических скульптур, извергающих огонь, до электронных штуковин и новых способов переработки материалов для создания дешевых вещей. Действительно, мейкер – это просто ремесленник, любитель или энтузиаст, который желает создавать вещи, – следовательно, творец. Таким образом, существует много разновидностей мейкеров. Их объединяет готовность поделиться своими методами и навыками с другими. Таким образом, чтобы по-настоящему стать мейкером, вы должны участвовать в сообществе мейкеров.
494  Куда двигаться дальше? Делитесь своими идеями Вы можете стать мастером и при этом вообще не вносить вклад в онлайнсообщества, и это совершенно нормально, но если вы хотите помочь сделать сообщество сильнее, вам следует принимать более активное участие. Лучший способ сделать это – присоединиться к одному или нескольким онлайн-форумам и начать вносить свой вклад. Это не значит, что вы должны начать с публикации какого-нибудь фантас­ тически крутого и успешного IoT-проекта, дополненного отмеченной наградами документацией и пуленепробиваемым кодом, который профессора информатики когда-нибудь будут использовать, чтобы учить молодые умы на примере ваших блестящих способностей. Конечно, вы можете посмеяться над этим, но я встречал людей, которые боятся что-либо публиковать, чтобы не ошибиться или не быть высмеянным за неточности. Не беспокойтесь об этом: лучший вклад вносят те, кто помогает вам расти, а вы растете, изучая более эффективные методы! Лучший способ избежать ляпов – начинать медленно, сначала задавая собственные вопросы. Вы также можете начать с нескольких положительных комментариев о тех идеях и проектах, которые вам нравятся. По мере того как вы будете более активно изучать темы и методы, ваши знания расширятся до такой степени, что вы сможете начать отвечать на вопросы других. Я рекомендую вам рассмотреть этот уровень участия, если вы хотите более активно участвовать в развитии IoT и MicroPython. Я считаю, что взаимодействие с сообществом и завоевание репутации полезного человека и обмена идеями – это одна из вещей, которая отличает любителя от энтузиаста. Таким образом, если вы хотите стать мейкером, вам следует делиться свои­ ми идеями с другими. Посещайте мероприятия Еще один способ стать более активным участником сообщества производителей – это посетить ярмарку производителей (makerfaire.com). Подобные мероприятия проводятся по всему миру. Мероприятия позволяют производителям демонстрировать свои творения, обучать других и в целом рекламировать то, что создано. Посетите сайт Maker Faire, чтобы узнать о мероприятиях в районе рядом с вами. Если вы живете в крупном городе или рядом с ним, вы можете обнаружить, что существуют локальные группы пользователей для Python, Raspberry Pi, MySQL, Arduino и даже группы пользователей-разработчиков. Попробуйте поискать мероприятия и группы в вашем районе и узнайте, где и когда они встречаются. Большинство групп придерживаются политики открытых дверей и приглашают всех присутствовать на своих собраниях. Некоторые, однако, взимают членские взносы для членов-учредителей, но это часто со-
Итоги  495 провождается дополнительными привилегиями, такими как доступ к инструментам, лабораториям и скидки на оптовые закупки. Побывав на мероприятии (даже таком, которое не имеет ничего общего с MicroPython, например встрече Raspberry Pi), вы можете многому научиться у других, и кто знает, возможно, однажды вы сами будете выступать на подобном мероприятии. Исходя из моего опыта, может быть очень приятно поделиться своими знаниями с другими на открытом форуме, таком как конференция, встреча группы пользователей или ярмарка Maker Faire. Именно тогда вы знаете, что приобрели репутацию, навыки и знания, которыми обладает типичный мейкер. СОВЕТ Многие пользователи Raspberry Pi используют Python в своих проектах. Не стесняйтесь искать встречи по Raspberry Pi, поскольку вы можете многому научиться у разработчиков Raspberry Pi Python. Это не означает, что к тому времени, когда вы прочтете данную главу, вы станете экспертом во всех областях интернета вещей или даже будете претендовать на звание эксперта, но вы все равно будете на верном пути. Итоги Поднять свои навыки работы с интернетом вещей на новый уровень – это то, к чему в конечном итоге будет стремиться большинство людей. Однако даже если вы не хотите становиться странствующим мастером, обучающим мир, вы можете многому научиться, просто присоединившись к онлайн-сообществам, которые занимаются Python, IoT и открытым оборудованием. В этой главе я представил сведения о том, как лучше всего взаимодействовать с онлайн-сообществами, где искать возможность присоединиться к ним и даже как довести свои навыки до высшего уровня энтузиазма и стать мас­ тером. Эта глава завершает наше путешествие по пути MicroPython для интернета вещей, который, я надеюсь, вдохновит вас продолжать практиковать свои навыки и в конечном итоге присоединиться к сообществу энтузиас­товединомышленников. Я искренне надеюсь, что эта книга открыла для вас много дверей, касающихся Python, MicroPython, интернета вещей и всего открытого оборудования. Пусть все ваши проекты будут успешными, а если нет, то пусть вы чему-то научитесь в процессе. Просто не забудьте поделиться своим опытом – хорошим или плохим – с миром и отдать немного того, что вы можете получить от усилий других.
Предметный указатель A S Adafruit, 262, 270, 274, 277, 279, 301, 304, 307, 356, 377, 381, 481 Adafruit IO, 20, 417, 419, 432, 435, 438, 442, 446 Arduino, 48, 51, 59, 98, 110, 114, 118 IDE, 78 Arduino IoT Cloud, 377, 382, 385 Arduino Nano RP2040, 100 Arduino Nano RP2040 Connect, 18, 52, 86, 98, 100, 104, 114, 201, 285, 286, 310, 325, 354, 374, 405, 434, 468, 479 aspberry Pi Pico, 64 Seeed Studio XIAO RP2040, 103 B BBC micro:bit, 104 BME280, 210, 352, 356 BMP180, 210 C CircuitPython, 108, 197 G T Thonny, 59, 78, 156, 164, 457 А Акселерометр, 99, 105, 261 Аналого-цифровой преобразователь (АЦП), 92, 252, 321, 328 Аудиодатчики, 261 Б Библиотека, 48 аппаратного уровня, 50, 199, 201, 204 пользовательская, 194 стандартная, 50, 169, 172 Arduino Nano RP2040 Connect, 202 MicroPython, 168, 190, 200 Python, 52, 168 Raspberry Pi Pico, 202 Биометрические датчики, 262 GPIO, 48, 64, 81, 87, 98, 100, 103 В R Видеодатчики, 272 Внешний модуль, 210, 252 Выводы GPIO, 48, 64, 81, 87, 98 Raspberry Pi, 78 Raspberry Pi Pico, 18, 42, 52, 59, 74, 81, 104, 174, 193, 201, 206, 282, 309, 325, 353, 374, 405, 434, 467 Raspberry Pi Pico W, 43, 52, 89, 193, 284 REPL, 50, 59, 70, 110, 156, 164, 457 RFID, 271 Д Датчик, 256 аналоговый, 258 биометрические, 262 влагосодержания, 268
Итоги  497 емкостные, 262 магнитометрические, 268 местоположения, 267 монет, 263 наклона, 272 погоды, 273 приближения, 269 радиации, 270 расхода жидкости, 266 света, 264 силы тока, 263 скорости, 271 тензорезистивные, 264 уровня жидкости, 266 цифровой, 259 BME280, 210, 352, 356 Диод, 243 Макетная плата, 66, 115, 253, 280, 309 Метеодатчики, 273 Н Напряжение, 248 измерение, 234 П Переключатели, 249 Предохранитель, 244 Прошивка (Firmware), 76, 101, 169, 200 Р Резисторы, 245, 308 Реле, 247 Е С Емкостные датчики, 262 Светодиоды (LED), 245, 308 Словари (dictionary), 128 Списки (lists), 129 Стабилизатор напряжения, 251 Считыватели магнитных полос, 267 штрих-кодов, 261 И Интерпретатор, 41, 49 Интерпретация, 49 Интерфейс, 88, 201, 322 прикладного программирования (API), 168, 202 Bluetooth (BLE), 203 I2C, 212, 290, 352 SPI, 218, 290 Исключения, 187 К Калибровка, 259, 323, 327 Кнопка, 241 с фиксацией, 241 Компилятор, 49 Компиляция, 49 Конденсатор, 242 Консоль REPL, 50, 59, 70, 110, 164, 457 Кортежи (tuple), 128 Т Тензорезистивные датчики, 264 Ток, 242, 248 датчики, 263 измерение, 236 номинальный, 245, 308 переменный (AC), 239 постоянный (DC), 234, 239 электрический, 239 Транзистор, 250 Ф Функция обратного вызова, 208, 314, 401, 439 М Ш Магнитометры, 268 Шифрование, 40, 100, 378
Чарльз Белл MicroPython для микроконтроллеров и проектов IoT
MicroPython для микроконтроллеров и проектов loT Эта книга поможет вам быстро и легко научиться программировать микро­ контроллеры и устройства loT - интернета вещей. Автор опирается на практический подход, не углубляясь в теорию. Вы изучите ряд проектов, в которых используется MicroPython - разновидность одного из наиболее широко используемых языков сценариев, и на основе этого материала сможете разрабатывать собственные lоТ-приложения. Основные темы: программы на MicroPython; принципы устройства датчиков и базовая электроника; разработка собственных lоТ-проектов; приложения для популярных плат на примере Raspberry Pi Pico и Arduino Nano Connect RP2040; загрузка MicroPython на совместимые платы; интерфейс с дополнительными аппаратными модулями; подключение оборудования через MicroPython; проекты интернета вещей в облаке. Книга адресована тем, кто мало знаком с электроникой и миром интернета вещей и хочет освоить простой способ работы без необходимости долго изу­ чать программирование на языках С++ или С.