Русский
Русский
English
Статистика
Реклама

Esp32

Перевод Умная крышка для мониторинга хлебной закваски

21.03.2021 12:21:06 | Автор: admin


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

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

В прошлом году я встретил ветку на Reddit, а также узнал о проекте sourd.io Кристины Суну. В обоих случаях авторы измеряли высоту закваски с помощью расположенного в крышке датчика расстояния. Мне это решение показалось гениальным, и я решил его повторить. Однако помимо онлайн-мониторинга я хотел также регистрировать данные с целью последующего анализа, для чего решил подключить датчик к интернету и считывать их напрямую. Сохранение на SD-карту я счел затратным и менее привлекательным в условиях современного мира, где практически все подключается по WiFi.

Если захотите собрать такое устройство сами все файлы проекта и код доступны на GitHub.


Три режима: максимальный подъем/время, график и статистика


Отбор, просмотр и скачивание данных для сессии кормления

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


Разработка


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

Электрическая часть


Все необходимые компоненты я заказал с Digikey и Aliexpress, а в качестве основы для подключения задействовал монтажную плату:

  • NodeMCU ESP8266;
  • датчик расстояния VL6180X;
  • датчик температуры и влажности DHT22;
  • OLED-дисплей SSD1306 128x32.

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


Прототип платы с готовыми модулями

Раньше я собирал устройства на макетных платах (например, контроллер приготовления продуктов методом су-вид (sous vide)), но это занимало очень много времени. И поскольку я уже научился работать в KiCad, то теперь предпочитаю несколько недель подождать доставки заморских плат, особенно с учетом их дешевизны ($2 за 5 экземпляров плюс пересылка).


Макет платы и схема


Слева изображена верхняя сторона платы с дисплеем, а справа нижняя сторона с датчиками расстояния и температуры/влажности

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


Выглядит неприглядно, зато работает

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

  • высоту банки;
  • начальную высоту закваски;
  • высоту, до которой закваска поднялась.

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



Значения:

  • d1=расстояние до дна банки;
  • d2=расстояние до начальной высоты закваски;
  • d3=расстояние до текущей высоты закваски;
  • h1=начальная высота закваски;
  • h2=текущая высота закваски.

Основные уравнения:

  • h1=d1d2h1=d1d2
  • h2=d2d3h2=d2d3
  • h_процент подъема=h2/h1=(d2d3)/(d1d2)

В результате я получил формулу для вычисления уровня подъема закваски в процентах.

Корпус


Корпус я разработал во Fusion 360, после чего напечатал его на своем Monoprice Mini 3D.



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

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


Все верно, это винт, приклеенный к кнопке включения

Как бы то ни было, но собралось все без проблем, и единственной заметной снаружи ошибкой стала торчащая шляпка винта. Можно сказать, что я намеренно таким образом придал конструкции стимпанковский вид.


Активная, здоровая закваска под внимательным мониторингом

Прошивка


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


Многозадачность выглядит как многопоточность (Source: FreeRTOS)

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


Легковесная реализация кооперативной многозадачности в TaskScheduler (источник: GitHub)

Код был поделен на следующие задачи/файлы:

  • measurements.cpp считывает данные с датчиков и производит измерения, доступные для других задач.
  • userinput.cpp обрабатывает нажатия кнопки пользователем (короткое нажатие, длинное и двойное).
  • display.cpp отображает информацию на дисплее.
  • iot.cpp отправляет данные измерений в облако.

Удобство этой архитектуры в том, что каждый файл содержит менее 200 строчек кода, а четкое разделение задач упрощает разработку и отладку.


Дисплей показывает, насколько поднялась закваска, и сколько с этого момента прошло времени

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

Подключение к облаку


Одной из самых долгих частей проекта стала настройка подключения ESP8266 к AWS. При этом возникло множество проблем совместимости библиотек, чего следовало ожидать, так как эта микросхема была выпущена 6 лет назад (на момент разработки). Если бы я не нашел ее в своем ящике с деталями, то использовал бы ESP32, с которой наверняка возникло бы куда меньше проблем.

Если вы решите воспроизвести проект, то позвольте избавить вас от лишней головной боли. Можете воспользоваться подготовленным мной шаблоном настройки, который также доступен на GitHub.

Как только мне удалось настроить передачу устройством сообщений через MQTT в AWS, я настроил облачную инфраструктуру на получение и сохранение передаваемых данных. Поток получился следующий:

  1. Устройство отправляет сообщение с данными через MQTT.
  2. В ответ на событие получения сообщения срабатывает функция AWS Lambda, считывая данные и передавая их в поток доставки Kinesis Firehose.
  3. Kinesis Firehose получает данные и сохраняет их на Amazon S3.


Архитектура AWS для простых IoT-приложений (источник: DZone)

Изначально я собирался использовать для визуализации данных Amazon QuickSight, но это предполагало ограничение скорости обновления, что для меня оказалось критическим. Вместо этого я, не страшась трудностей, создал собственную информационную панель с помощью Flask и HTML/CSS/JavaScript, которая запрашивает данные из S3, используя Amazon Athena. Ввиду своей чрезмерной лени я не стал выяснять, как разместить эту панель на AWS бесплатно и решил использовать Heroku (как делал это ранее для своего онлайн-калькулятора рецептов).

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

Анализ


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

Общие выводы


За несколько недель мониторинга мне удалось собрать достаточное количество информации. Интересовало же меня два основных момента:

  1. Изменяется ли максимальная высота закваски по ходу повторяющихся кормлений.
  2. Будет ли закваска расти также, если поместить ее в холодильник и не кормить несколько дней.

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


График ядерной плотности, показывающий распределение и кластеризацию данных. Очевидные корреляции отсутствуют

Приведенный ниже график отражает последовательность кормлений:

  • 5 последовательных кормлений, начиная с 21 января;
  • 3 последовательных кормления: 30 января, 3 февраля и 17 февраля;
  • 2 последовательных кормления 12 февраля.

Примечание: горизонтальная ось немного смещена, так как в некоторые дни я делал несколько кормлений, а график предполагает, что каждое кормление происходит в отдельный день.


График кормления на протяжении нескольких недель

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


Статистической значимости между наиболее важными метриками не наблюдается

Подробности


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

  • Пиковая высота подъема возросла со ~100% до ~200% к 6-му последовательному кормлению.
  • Уклон при увеличении высоты подъема не изменялся, в том числе при нахождении закваски в холодильнике.
  • Пиковая высота подъема достигается к ~5 часам и сохраняется в течение ~3-5 часов.

Примечание: я использовал расстойную камеру, в связи с чем температура и влажность сохранялись постоянными практически весь процесс ферментации. На протяжении большинства дней температура была установлена на 24С, но интересно то, что регистрировалось, как правило, около 30C. Для определения, какой из датчиков врет, нужно дополнительное тестирование.


Высота подъема, температура и влажность с течением времени


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

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

  • 30 января: все кормления дали относительно одинаковый результат, даже первое после извлечения закваски из холодильника, где она находилась 5 дней.
  • 3-5 февраля: после второго кормления сформировался более крутой уклон. Думаю, что в этот раз я изменил пропорции подкормки с 1:2:2 на 1:3:3, но точно не помню (а может в тот раз кормление делала жена?). Также интересно, что температура для третьего кормления не была постоянной. Может, расстойная камера была отключена?
  • 12 февраля: после второго кормления подъем оказался на 100% выше, чем после первого. Скорее всего, пропорции подкормки были изменены. Кроме того, при первом кормлении также весьма странно себя вел показатель влажности.
  • 17-18 февраля: после первого кормления подъем происходил дольше, чем после последующих двух, что опровергает мои наблюдения от 30 января



Визуализация остальных данных о кормлениях (можно нажать для полного размера)

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

  • пропорции подкормки;
  • начальную и конечную температуру закваски (с помощью кулинарного термометра);
  • жесткость воды (необязательно).

Помимо этого, я хочу провести кое-какие дополнительные эксперименты:

  • сравнить пропорции ингредиентов подкормок и/или замесов теста;
  • сравнить скорость роста в/вне среды с контролируемой температурой (то есть расстойной камеры);
  • найти идеальное место при условии отсутствия расстойной камеры (Духовка с включенным светом? Микроволновка с теплой водой?).


Заключение


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

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

Я считаю, что всех пекарей можно поделить на две категории: тех, кто готовит по ощущениям, и тех, кто пользуется кухонными весами с точностью 0.01 г. Угадайте, к каким отношусь я ;)


Я называю это чертовски хороший мякиш!

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

Удачной выпечки!

Подробнее..

Фриланс-разработка электроники. Что, Как и Зачем?

27.05.2021 12:14:32 | Автор: admin

Как все начиналось


Начну немного издалека, чтобы было понятно с чего все изначально началось.
Электронику я люблю с детства, родители военные и они же инженеры привили любовь не только к морзянке, но и к электронике. За что им отдельное спасибо.
После окончания ВУЗа, работаю по сей день ведущим инженером на одной из атомных станций. Работа веселая и ответственная, но дома надо чем-то занять вечера, не пиво же пить и лежать у телевизора. Для поддержания на должном уровне своей квалификации освоил Ардуино, Attiny, STM, ESP32. Вспомнил давно забытую Java и C++. Освоил заново Easyeda, Altium, Eagle. Свободно работаю во многих программах CAD моделирования. Теоретически я подготовлен хорошо, но нужна была практика в электронике и желательно по очень высоким стандартам.

Практика в разработке электроники


А где её ПРАКТИКУ искать если не во фрилансе?

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

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

Свой первый заказ на разработку электроники я получил из США, нужно было разработать максимально возможную миниатюрную плату клавиатуры 3*4. Работа была приятная и полезная, т.к. заказчик очень доброжелателен и готов объяснить, что ему нужно хоть сто раз подряд, даже если вы говорите заикаясь и только с Гугл переводчиком.
При заказе печатных плат со сборкой на Jlcpcb.com у заказчика возникли проблемы, т.к. элементы оказались очень маленькими. Заказчик быстро сориентировался и нашел того изготовителя которых смог сделать печатную плату со сборкой. Имея положительный опыт первой выполненной работы, последовали и другие работы. Очень быстро мы с моим первым Заказчиком нашли общий язык и понимали уже друг друга с полуслова.

Вот какие клавиатуры получились в конечном варианте:


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

Работа по срокам длительная, более 6 месяцев.

Задача: с нуля создать специфическое устройство под требования заказчика. Устройство батарейного типа (литиевый аккумулятор), с зарядкой и стабилизатором на борту. Микроконтроллер желательно с WiFi, Bluetooth и BLE. Управление светодиодами и вибромотором. Плюс к этому устройство должно уметь считывать RFID метки по стандарту ISO 15693 на частоте соответственно 13,56 МГц.

И все это нужно расположить на плате размерами не более 70 на 30 мм. По высоте ограничение 10 мм вместе с аккумулятором. По токовым нагрузкам, устройство должно работать минимум 10 часов без подзарядки, т.е. все лишнее должно быть выключено. И, пожалуй, самое главное и сложное условие Заказчика устройство должно пройти процедуру сертификации FCC Федеральная комиссия по связи США, т.к. устройство предполагается распространять в США, у них с этим очень строго.
Процедура не дешевая, поэтому все компоненты нужно максимально совместить не только в различных диапазонах, но и при необходимости провести экранирование слишком шумящих элементов.
Скурив множество манов на множество чипов со встроенным и отдельными элементами для данного устройства, по контроллеру выбор пал на ESP32-WROOM умеет работать с блютузом в режиме экономии (BLE), 2 ядра, портирован на FREERTOS, SPI, I2C и много чего еще в наличии и на борту плюс ко всему сертифицирован FCC. RFID читалка PN5180 от NXP, неприхотливая, рабочая лошадка, при наличии векторного анализатора вполне успешно настраивается под любую антенну, достаточно полное описание внутренностей, портирован на С, много примеров на сайте NXP (правда под LPC контроллеры) и самое главное пришлось Заказчику покупать их в США, т.к. с последней прошивкой PN5180 в Россию не поставляются, от слова совсем.

Программирование велось под MS Visual Studio с плагинами VMicro под ESP32, операционка FREERTOS (т.к. работает в многозадачной режиме). Чтение RFID, управление светодиодами и вибромотором предполагало передачу данных по блютузу на Android и Iphone нужна была среда для разработки приложения выбор пал на x.thunkable.com что полностью себя оправдало.

Первая стадия проекта включала в себя проработку схемных решений, опробование различных вариантов подключения, распределения ресурсов, обоснованного отказа от использования того или иного чипа или решения. Еще до заключения контракта мы больше недели по вечерам общались и обсуждали, это было незабываемо. Заказчик по нашему контракту заказал всю элементную базу для фазы проработки, тестирования и отладки. Пока все путешествовало через полмира из США в Россию, я сидел за манами:


и рисовал начальный вариант схемы в Easyeda.com


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

Для прошивки и отладки был использован мост USB-to-UART CP2104 маленький, шустрый, полностью покрывающий потребности.


Корпус QFN EP-24 Паяется отлично, главное все правильно сделать.

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

А не развести ли нам печатную плату?


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

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


Тот, кто пользуется Sprint layout меня поймет. Когда сделал лучше, чем автомат. Мозг тренируется и ликует, вы радуетесь. Значит ваше умение разводить печатные платы растет. Интереснейшая головоломка, зависнуть можно на неделю, а то и больше. Короче, разводка печатной платы это просто удовольствие.

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


Вот так Easyeda показывает 3D модель печатной платы.

Заказ печатных плат для устройства 4 слойные с повышенной точностью совместимости по слоям за счет применения материала стеклотекстолита FR-4 TG-155.



Компонентная база наше всё


Отдельно скажу о компонентах, в манах приводятся компоненты которые рекомендует производитель, лучше придерживаться рекомендаций или брать лучше.
Мне повезло, я долгое время работаю по компонентам с Electronshik.ru/
База огромная, цены адекватные если не ниже. 95 процентов всех элементов, доступно немедленно, т.е. после оплаты заказа, отправка сегодня-завтра. Доставка DHL до двери 280 рублей (До моей двери именно так). Меня это очень поразило в первые разы. Приятно работать с такой компанией, это вам не чипдип.

Так же некоторые элементы заказывались у партнера Easyeda Lcsc.com при заказе плат, можно прикрепить к заказу и элементы, купленные у партнера. Экономия на доставке.

Пайка, всякая и разная


На эту тему написано и снято видео столько что можно библиотеку создать. Корпуса элементов маленькие от 0402 до 0805. Плата тестовая была зафлюсована, залужена, потом сама пайка. Нюансы пайки на ютьюбе. И да, я паяю под микроскопом.



Первое включение или порядок сборки


Порядок пайки у меня такой:

  1. USB разъем
  2. микропереключатель
  3. CP2104 с обвязкой
  4. Подача питания от блока питания
  5. определение компьютером нового USB устройства
  6. Пайка чипа зарядки литиевой батареи и стабилизатора с обвязкой
  7. Проверка в связке CP2104, зарядки и стабилизатора


На очереди ESP32. Нюанс с пайкой корпуса на 39 ножке, ребят не ленитесь его паять, так написано в мане, не зря же вы его разводили на плате. Аккуратненько запаиваем как по учебнику все ножки ESP32 и его обвязку. Проверяем работу зарядки, стабилизатора и ESP32.

Первая прошивка контроллера тестовой прошивкой проверка WiFi и блютуз. но не происходит сброса перед прошивкой. Забыл на плате, схему сброса развести (в последней версии исправлено этот недочет и еще парочку не критичных), припаялся прямо к ESP32, прошил успешно.

Остается самое сложное по железу, пайка PN5180 и его обвязки под радиочастотную часть. Частота небольшая 13.56МГц, но требования к аккуратности пайки еще никто не отменял. Все должно быть красиво и радовать глаз.

До пайки PN5180 сделал дополнительные расчеты в RFSim99, а также в фирменной программе NXP NFC Antenna Tool, чтобы точнее попасть по параметрам согласования антенны и радиочастотной части PN5180.

Программы помогли, но настраивать под реальные условия все равно пришлось с помощью векторного анализатора OSA-103 (Заказчик закупил на этап настройки). КСВ антенны получил 1.17. Оса-103 отечественная разработка, отличного качества, целая лаборатория в одном приборе. Дополнительные конденсаторы и резисторы Электронщик доставил очень быстро, так что никаких задержек с дальнейшей пайкой не было.

Все запаяно, несколько раз проверено, включение микропереключателя, определение как USB устройства, компиляция теста для проверки PN5180 попытка заливки прошивки. Заливка прошивки. Чтение данных с RFID NFC карточки. Ура с железом закончили, все работает, как и планировалось.

Программная часть, последняя


Написание программной части это как порыв страсти, как дзен. Садишься за клавиатуру и по заранее написанному алгоритму, пишешь код. Строчка за строчкой, программа обрастает задачами, функциями, процедурами, тестовыми модулями.
Весь код писался под FREERTOS из-за его гибкости. Каждой задаче таске свое время будет выделено. Планировщик все сделает для этого. Под чистый Ардуино-код это сделать сложно, т.к. есть основная задача которая крутится в цикле loop не сможет управлять периферией, слушать окружение антенны NFC и обмениваться данными по блютузу. Вернее так, можно, но не так красиво и изящно, как под FREERTOS.
Устройство закончено, остались нюансы по прошивке для уточнения под требования Заказчика.

В заключении, своего повествования хочу сказать, что разработка электроники даже на фрилансе это реальность. Это возможно и это нужно. Ведь в мире столько нового и интересного. И заметьте мне всего 47 лет :) Жизнь движение.

P.S. Если у кого-то возникли вопросы пишите в комментариях.


Подробнее..

Espressif IoT Development Framework 71 выстрел в ногу

20.01.2021 16:09:28 | Автор: admin

0790_Espressif_IoT_Development_Framework_ru/image1.png
Один из наших читателей обратил наше внимание на Espressif IoT Development Framework. Он нашёл ошибку в коде проекта и поинтересовался, смог бы её найти статический анализатор PVS-Studio. Именно эту ошибку анализатор пока найти не может, зато нашёл множество других. По мотивам этой истории и найденных ошибок, мы решили написать классическую статью про проверку открытого проекта. Приятного изучения того, из-за чего IoT устройства могут "выстрелить вам в ногу".


Программно-аппаратные системы


Отец языка C++ Бьярне Страуструп как-то сказал:


"Си" позволяет очень просто выстрелить себе в ногу. На "Си++" сделать это сложнее, но, когда вы это делаете, отрывает всю ногу.

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


Такие проекты, как Espressif IoT Development Framework, служат для реализации программно-аппаратных систем, взаимодействующих с человеком и управляющие объектами в реальном мире. Всё это накладывает дополнительные требования к качеству и надёжности программного кода. Именно отсюда берут основы такие стандарты как MISRA или AUTOSAR. Впрочем, это уже другая тема.


Вернёмся к Espressif IoT Development Framework (исходный код на сайте GitHub: esp-idf). Вот его краткое описание:


ESP-IDF is Espressif's official IoT Development Framework for the ESP32 and ESP32-S series of SoCs. It provides a self-sufficient SDK for any generic application development on these platforms, using programming languages such as C and C++. ESP-IDF currently powers millions of devices in the field, and enables building a variety of network-connected products, ranging from simple light bulbs and toys to big appliances and industrial devices.

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


Предыстория


Ещё хочется рассказать, как появилась эта статья. Мне написал Юрий Попов (Hardcore IoT fullstack dev & CTO), который с интересом следит за нашими публикациями. Незадолго до этого он самостоятельно вручную нашёл ошибку в Espressif IoT Development Framework и поинтересовался, может ли выявить этот дефект PVS-Studio. Ошибка связана с опечаткой коде, а PVS-Studio всегда славился тем, что хорошо выявляет подобные ошибки.


Некорректный код находился в файле mdns.c:


mdns_txt_linked_item_t * txt = service->txt;while (txt) {  data_len += 2 + strlen(service->txt->key) + strlen(service->txt->value);  txt = txt->next;}

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


Правильный код:


data_len += 2 + strlen(txt->key) + strlen(txt->value);

К обоюдному разочарованию меня и читателя Юры, PVS-Studio не смог заметить эту ошибку. Он просто не знает про такой паттерн ошибки. Собственно, и наша команда не знала про такой паттерн. PVS-Studio, как и любой другой анализатор, умеет замечать только то, на что его запрограммировали :).


Что ж, жаль, но не страшно. Это один из источников, где можно черпать идеи по развитию PVS-Studio. Пользователи и клиенты присылают различные паттерны ошибок, которые они выявили в коде своих проектов, но про которые не знает PVS-Studio. И мы постепенно создаём новые диагностические правила. Так произойдёт и с рассмотренным выше паттерном. Мы уже выписали этот пример в TODO и реализуем новое диагностическое правило для выявления схожих ситуаций в одной из следующих версий анализатора.


По итогам всего этого, Юра сам написал небольшую заметку про эту ошибку, как он её искал и про PVS-Studio: "Баг в ESP-IDF: MDNS, Wireshark и при чём тут единороги". Плюс он уведомил авторов проекта о найденной ошибке: Spurious MDNS collision detection (IDFGH-4263).


На этом история не закончилось. Юра предложил нашей команде проверить проект и написать заметку о результатах. Мы не стали отказываться, так как весьма часто делаем подобные публикации для популяризации методологии статического анализа кода и заодно инструмента PVS-Studio :).


Правда проверку мы повели достаточно неуклюже. К сожалению, нет примера "собрать всё". Ну или мы не разобрались. Мы начали с getting_started\hello_world. Вроде бы он использует часть фреймворка, но не полностью. Так что можно найти и другие ошибки, добившись компиляции большего количества файлов фреймворка. Другими словами, то, что в статье будет описана только 71 ошибка, это наша недоработка :).


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


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


Примеры, откуда берутся ложные/бессмысленные срабатывания


Всех исследователей, которые захотят проверить Espressif IoT Development Framework, я хочу предупредить, что понадобится предварительная настройка анализатора. Без неё вы утоните в большом количестве ложных/бесполезных срабатываний. Но анализатор не виноват.


В коде проекта очень активно используются директивы условной компиляции (#ifdef) и макросы. Такой стиль кодирования запутывает анализатор и порождает множество однотипных бесполезных предупреждений. Чтобы было понятнее, как и почему это происходит, рассмотрим пару примеров.


Предупреждение PVS-Studio: V547 Expression 'ret != 0' is always true. esp_hidd.c 45


esp_err_t esp_hidd_dev_init(....){  esp_err_t ret = ESP_OK;  ....  switch (transport) {#if CONFIG_GATTS_ENABLE  case ESP_HID_TRANSPORT_BLE:    ret = esp_ble_hidd_dev_init(dev, config, callback);    break;#endif /* CONFIG_GATTS_ENABLE */  default:    ret = ESP_FAIL;    break;  }  if (ret != ESP_OK) {    free(dev);    return ret;  }  ....}

Выбран такой режим компиляции, при котором макрос CONFIG_GATTS_ENABLE не объявлен. Поэтому, для анализатора этот код выглядит так:


esp_err_t ret = ESP_OK;....switch (transport) {default:  ret = ESP_FAIL;  break;}if (ret != ESP_OK) {

Анализатор вроде прав, что условие всегда истинно. С другой стороны, пользы от этого предупреждения нет, так как мы понимаем, что код совершенно корректен и имеет смысл. Подобные ситуации встречаются крайне часто, что затрудняет просмотр отчёта. Это такая неприятная плата за активное использование условной компиляции :).


Рассмотрим другой пример. В коде активно используется своя разновидность assert-макросов. К сожалению, они тоже сбивают анализатор с толку. Предупреждение PVS-Studio: V547 Expression 'sntp_pcb != NULL' is always true. sntp.c 664


#define LWIP_PLATFORM_ASSERT(x) do \  {printf("Assertion \"%s\" failed at line %d in %s\n", \    x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)#ifndef LWIP_NOASSERT#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \  LWIP_PLATFORM_ASSERT(message); }} while(0)#else  /* LWIP_NOASSERT */#define LWIP_ASSERT(message, assertion)#endif /* LWIP_NOASSERT */sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);if (sntp_pcb != NULL) {

Анализатор видит, что код в которой раскрывается LWIP_ASSERT остановит выполнение программы (см. вызов функции abort), если указатель sntp_pcb будет нулевой. Поэтому PVS-Studio предупреждает, что следующая проверка (sntp_pcb != NULL) не имеет смысла.


С одной стороны, анализатор прав. Но всё поменяется, если макрос в другом режиме компиляции будет раскрыт в "ничто". В этом случае проверка уже будет иметь смысл. Да, при втором сценарии анализатор ругаться не будет, но сути это уже не меняет. В первом то случае у нас лишнее предупреждение.


Впрочем, всё это не страшно. Если взяться за проект всерьез, то можно настроить анализатор так, что большинство бессмысленных сообщений исчезнет. Ещё в ряде мест можно улучшить ситуацию, изменив стиль написания кода и макросов. Но это уже выходит за рамки данной статьи. Дополнительно можно использовать механизм подавления предупреждений в конкретных местах, в макросах и т.д. Есть ещё и механизм массовой разметки. Про всё это подробнее можно почитать в статье "Как внедрить статический анализатор кода в legacy проект и не демотивировать команду".


Security


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


Для удобства классификации слабостей кода можно использовать CWE (Common Weakness Enumeration). В PVS-Studio можно включить отображение CWE ID для предупреждений. Для предупреждений этой главы я дополнительно приведу соответствующий CWE ID.


Подробнее тема поиска потенциальных уязвимостей раскрыта в статье "Статический анализатор кода PVS-Studio как защита от уязвимостей нулевого дня".


Ошибка N1; Порядок аргументов


Предупреждение PVS-Studio: V764 Possible incorrect order of arguments passed to 'crypto_generichash_blake2b__init_salt_personal' function: 'salt' and 'personal'. blake2b-ref.c 457


int blake2b_init_salt_personal(blake2b_state *S, const uint8_t outlen,                               const void *personal, const void *salt);intblake2b_salt_personal(uint8_t *out, const void *in, const void *key,                      const uint8_t outlen, const uint64_t inlen,                      uint8_t keylen, const void *salt, const void *personal){  ....  if (blake2b_init_salt_personal(S, outlen, salt, personal) < 0)    abort();  ....}

При вызове функции blake2b_init_salt_personal перепутаны местами аргументы personal и salt. Мне кажется, вряд ли это задумано специально и, скорее всего, это ошибка, возникшая по невнимательности. Я не ориентируюсь в коде проекта и в криптографии, но что-то мне подсказывает, что такая путаница может иметь нехорошие последствия.


Согласно CWE эта ошибка классифицируется как CWE-683: Function Call With Incorrect Order of Arguments.


Ошибка N2; Отбрасывание значащих бит


Предупреждение PVS-Studio: V642 Saving the 'memcmp' function result inside the 'unsigned char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. mbc_tcp_master.c 387


static esp_err_t mbc_tcp_master_set_request(  char* name, mb_param_mode_t mode, mb_param_request_t* request,  mb_parameter_descriptor_t* reg_data){  ....  // Compare the name of parameter with parameter key from table  uint8_t comp_result = memcmp((const char*)name,                               (const char*)reg_ptr->param_key,                               (size_t)param_key_len);  if (comp_result == 0) {  ....}

Сохранять результат работы функции memcmp в однобайтовую переменную это очень плохо. Это дефект, который вполне может превратиться в реальную уязвимость, подобную этой: CVE-2012-2122. Подробнее, почему так писать нельзя, описано в документации к диагностике V642.


Если совсем кратко, то некоторые реализации функция memset могут возвращать в случае несовпадения блоков памяти не только значения 1 или -1. Функция, например, может вернуть значение 1024. А это число, записанное в переменную типа uint8_t превратится в 0.


Согласно CWE эта ошибка классифицируется как CWE-197: Numeric Truncation Error.


Ошибка N3 N20; Приватные данные остаются в памяти


Предупреждение PVS-Studio: V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 854


#ifndef os_memset#define os_memset(s, c, n) memset(s, c, n)#endifstatic int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,       unsigned int hash_len){  u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];  const char *info = "first intermediate key";  int res;  /* k1 = HKDF(<>, "first intermediate key", M.x) */  /* HKDF-Extract(<>, M.x) */  os_memset(salt, 0, hash_len);  if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)    return -1;  wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",      prk, hash_len);  /* HKDF-Expand(PRK, info, L) */  res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);  os_memset(prk, 0, hash_len);             // <=  if (res < 0)    return -1;  wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",                  k1, hash_len);  return 0;}

Очень распространённая ошибка. Компилятор вправе в целях оптимизации удалить вызов функции memset, так как после заполнения буфера нулями, он больше не используется. В результате приватные данные на самом деле не затираются, а продолжат болтаться где-то в памяти. Подробности можно узнать в статье "Безопасная очистка приватных данных".


Согласно CWE эта ошибка классифицируется как CWE-14: Compiler Removal of Code to Clear Buffers.


Другие ошибки этого типа:


  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 883
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 942
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'psk' buffer. The memset_s() function should be used to erase the private data. dpp.c 3939
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'prk' buffer. The memset_s() function should be used to erase the private data. dpp.c 5729
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'Nx' buffer. The memset_s() function should be used to erase the private data. dpp.c 5934
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'val' buffer. The memset_s() function should be used to erase the private data. sae.c 155
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'keyseed' buffer. The memset_s() function should be used to erase the private data. sae.c 834
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'keys' buffer. The memset_s() function should be used to erase the private data. sae.c 838
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'pkey' buffer. The memset_s() function should be used to erase the private data. des-internal.c 422
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'ek' buffer. The memset_s() function should be used to erase the private data. des-internal.c 423
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'finalcount' buffer. The memset_s() function should be used to erase the private data. sha1-internal.c 358
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'A_MD5' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 95
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'P_MD5' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 96
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'A_SHA1' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 97
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'P_SHA1' buffer. The memset_s() function should be used to erase the private data. sha1-tlsprf.c 98
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'T' buffer. The memset_s() function should be used to erase the private data. sha256-kdf.c 85
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'hash' buffer. The memset_s() function should be used to erase the private data. sha256-prf.c 105

Ошибка N21; Не удаляется буфер с приватными данными


Предупреждение PVS-Studio: V575 The null pointer is passed into 'free' function. Inspect the first argument. sae.c 1185


static int sae_parse_password_identifier(struct sae_data *sae,           const u8 *pos, const u8 *end){  wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",        pos, end - pos);  if (!sae_is_password_id_elem(pos, end)) {    if (sae->tmp->pw_id) {      wpa_printf(MSG_DEBUG,           "SAE: No Password Identifier included, but expected one (%s)",           sae->tmp->pw_id);      return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;    }    os_free(sae->tmp->pw_id);    sae->tmp->pw_id = NULL;    return WLAN_STATUS_SUCCESS; /* No Password Identifier */  }  ....}

Если с паролем что-то не так и указатель pw_id не нулевой, то выводится отладочное предупреждение и функция завершает свою работу. Что интересно, далее происходит попытка освободить буфер, используя нулевой указатель. Более того, в нулевой указатель вновь записывается NULL. Всё это не имеет смысла. Скорее всего, строчки освобождения памяти находятся не на своём месте. И мне кажется, код должен быть таким:


if (!sae_is_password_id_elem(pos, end)) {  if (sae->tmp->pw_id) {    wpa_printf(MSG_DEBUG,         "SAE: No Password Identifier included, but expected one (%s)",         sae->tmp->pw_id);    os_free(sae->tmp->pw_id);    sae->tmp->pw_id = NULL;    return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;  }  return WLAN_STATUS_SUCCESS; /* No Password Identifier */}

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


Согласно CWE, эта формально ошибка классифицируется как CWE-628: Function Call with Incorrectly Specified Arguments. Так её классифицирует PVS-Studio, но, по сути и последствиям, это какая-то другая слабость кода.


Ошибка N22, N23; Использование неинициализированного буфера в качестве ключа


Предупреждение PVS-Studio: V614 Uninitialized buffer 'hex' used. Consider checking the second actual argument of the 'memcpy' function. wps_registrar.c 1657


int wps_build_cred(struct wps_data *wps, struct wpabuf *msg){  ....  } else if (wps->use_psk_key && wps->wps->psk_set) {    char hex[65];    wpa_printf(MSG_DEBUG,  "WPS: Use PSK format for Network Key");    os_memcpy(wps->cred.key, hex, 32 * 2);    wps->cred.key_len = 32 * 2;  } else if (wps->wps->network_key) {  ....}

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


В любом случае этот код нуждается во внимательной проверке.


Согласно CWE, эта ошибка классифицируется как CWE-457: Use of Uninitialized Variable.


Аналогичная ошибка: V614 Uninitialized buffer 'hex' used. Consider checking the second actual argument of the 'memcpy' function. wps_registrar.c 1678


Опечатки и Copy-Paste


Ошибка N24; Copy-Paste классический


Предупреждение PVS-Studio: V523 The 'then' statement is equivalent to the 'else' statement. timer.c 292


esp_err_t timer_isr_register(....){  ....  if ((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) {    intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;  } else {    intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;  }  ....}

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


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


Ошибка N25; Не там поставлена скобка


Предупреждение PVS-Studio: V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. esp_tls_mbedtls.c 446


esp_err_t set_client_config(....){ .... if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0)) {   ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);   ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);   return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED; } ....}

Приоритет оператора сравнения выше, чем приоритет оператора присваивания. Поэтому условие вычисляется следующим образом:


TEMP = mbedtls_ssl_conf_alpn_protocols(....) != 0;if ((ret = TEMP))  PRINT(...., -ret);

В принципе, ошибочная ситуация поймается и обработается в коде, но не так, как задумано. Предполагалось распечатывать статус ошибки, который хранится в переменной ret. Но значение ret всегда будет равно 0 или 1. Поэтому, если что-то пойдёт не так, всегда будет распечатываться только одно значение (-1).


Ошибка возникла из-за того, что не там поставлена скобочка. Правильный код:


if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos)) != 0)

Теперь всё будет вычисляться как нужно:


ret = mbedtls_ssl_conf_alpn_protocols(....);if (ret != 0)  PRINT(...., -ret);

Рассмотрим ещё один очень похожий случай.


Ошибка N26; MP_MEM превращается в MP_YES


V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. libtommath.h 1660


В начале рассмотрим некоторые константы. Они пригодятся нам чуть ниже.


#define MP_OKAY       0   /* ok result */#define MP_MEM        -2  /* out of mem */#define MP_VAL        -3  /* invalid input */#define MP_YES        1   /* yes response */

Далее следует сказать, что существует функция mp_init_multi, которая может возвращать значения MP_OKAY и MP_MEM:


static int mp_init_multi(mp_int *mp, ...);

И теперь собственно код с ошибкой:


static intmp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d){  ....  /* init our temps */  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {     return res;  }  ....}

Рассмотрим проверку более тщательно:


if ((res = mp_init_multi(....) != MP_OKAY))

Вновь не там поставлена скобка. Поэтому в начале вычисляется:


TEMP = (mp_init_multi(....) != MP_OKAY);

Значение TEMP может быть только 0 или 1. Этим числам соответствуют константы MB_OKAY и MP_YES.


Далее выполняется присваивание и одновременно проверка:


if ((res = TEMP))   return res;

Видите подвох? Статус ошибки MP_MEM (-2) вдруг превратился в статус MB_YES (1). Последствия предсказать не могу, но ничего хорошего в этом нет.


Ошибка N27; Забыли разыменовать указатель


Предупреждение PVS-Studio: V595 The 'outbuf' pointer was utilized before it was verified against nullptr. Check lines: 374, 381. protocomm.c 374


static int protocomm_version_handler(uint32_t session_id,                                     const uint8_t *inbuf, ssize_t inlen,                                     uint8_t **outbuf, ssize_t *outlen,                                     void *priv_data){    protocomm_t *pc = (protocomm_t *) priv_data;    if (!pc->ver) {        *outlen = 0;        *outbuf = NULL;                                  // <=        return ESP_OK;    }    /* Output is a non null terminated string with length specified */    *outlen = strlen(pc->ver);    *outbuf = malloc(*outlen);                           // <=    if (outbuf == NULL) {                                // <=        ESP_LOGE(TAG, "Failed to allocate memory for version response");        return ESP_ERR_NO_MEM;    }    memcpy(*outbuf, pc->ver, *outlen);    return ESP_OK;}

Сообщение анализатора, на первый взгляд, может показаться непонятным. Давайте разбираться.


Если указатель pc->ver является нулевым, то функция досрочно завершает свою работу и при этом записывает значение по адресу, хранящегося в указателе outbuf:


*outbuf = NULL;

Запись по этому адресу происходит и далее:


*outbuf = malloc(*outlen);

А не нравится анализатору то, что затем этот указатель проверяется:


if (outbuf == NULL)

Действительно, это неправильно, в начале разыменовывать указатель, а только потом его проверять. Ляп в том, что на самом то деле должны были проверять не сам указатель, а то, что в него записали. Здесь просто в проверке опечатались и пропустили оператор разыменования (*).


Правильный код:


*outbuf = malloc(*outlen);if (*outbuf == NULL) {  ESP_LOGE(TAG, "Failed to allocate memory for version response");  return ESP_ERR_NO_MEM;}

Ошибка N28; Повторное присваивание


Предупреждение PVS-Studio: V519 The 'usRegCount' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 186, 187. mbfuncholding.c 187


eMBExceptioneMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ){  ....  USHORT          usRegCount;  ....  usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );  usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );  ....}

Код явно писался методом Copy-Paste. Строчку скопировали, но изменили только частично. По соседству есть вот такой осмысленный код:


usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF] << 8 );usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF + 1] );

Видимо, и в рассмотренном коде с ошибкой, следовало в первой строке использовать оператор =, а во второй оператор |=.


Логические ошибки


Ошибка N29 N31; Неправильная работа с кодами возврата (Rare)


Предупреждение PVS-Studio: V547 Expression is always false. linenoise.c 256


static int getColumns(void) {  ....  /* Restore position. */  if (cols > start) {    char seq[32];    snprintf(seq,32,"\x1b[%dD",cols-start);    if (fwrite(seq, 1, strlen(seq), stdout) == -1) {      /* Can't recover... */    }    flushWrite();  }  ....}

Это безобидный вариант неправильной обработки статуса, возвращаемого функцией. Ошибка безобидна в том смысле, что никакой обработки и не предусмотрено. Не получилось записать строчку, так не получилось :). Хотя, как я уже сказал, этот безобидный вариант, такой стиль написания программ явно не является образцом для подражания.


Суть же самой ошибки в том, что функция fwrite не возвращает статус -1. Это физически невозможно, так как функция fwrite возвращает значение целочисленного типа size_t:


size_t fwrite( const void *restrict buffer, size_t size, size_t count,               FILE *restrict stream );

А вот что возвращает эта функция:


The number of objects written successfully, which may be less than count if an error occurs.

If size or count is zero, fwrite returns zero and performs no other action.

Таким образом, проверка статуса является неверной.


Аналогичные места безобидной неправильной проверки статуса:


  • V547 Expression is always false. linenoise.c 481
  • V547 Expression is always false. linenoise.c 569

Ошибка N32, N33; Неправильная работа с кодами возврата (Medium)


Предупреждение PVS-Studio: V547 Expression is always false. linenoise.c 596


int linenoiseEditInsert(struct linenoiseState *l, char c) {  ....  if (fwrite(&c,1,1,stdout) == -1) return -1;  ....}

Хотя перед нами та же ошибка, что и в предыдущем случае, она более серьезна. Если не удаётся записать символ в файл, то функция linenoiseEditInsert должна прекратить свою работу и вернуть статус -1. Но этого не произойдёт, так как fwrite никогда не вернёт значение -1. Перед нами логическая ошибка обработки ситуации, когда не удаётся что-то записать в файл.


Аналогичную ошибку можно найти здесь: V547 Expression is always false. linenoise.c 742.


Ошибка N34; Неправильная работа с кодами возврата (Well Done)


Предупреждение PVS-Studio: V547 Expression is always false. linenoise.c 828


static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)  ....  while(1) {    ....    if (fread(seq+2, 1, 1, stdin) == -1) break;    ....  }  ....}

Ошибка в том, что, как и в случае с fwrite, функция fread не возвращает в качестве статуса значение -1.


size_t fread( void *restrict buffer, size_t size, size_t count,              FILE *restrict stream );

Return value

Number of objects read successfully, which may be less than count if an error or end-of-file condition occurs.

If size or count is zero, fread returns zero and performs no other action.

fread does not distinguish between end-of-file and error, and callers must use feof and ferror to determine which occurred.

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


Ошибка N35; Использование оператора || там, где нужен оператор &&


Предупреждение PVS-Studio: V547 Expression is always true. essl_sdio.c 209


esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms){  ....  // Set block sizes for functions 1 to given value (default value = 512).  if (ctx->block_size > 0 || ctx->block_size <= 2048) {    bs = ctx->block_size;  } else {    bs = 512;  }  ....}

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


Итак, перед нами всегда истинное условие. Ведь некая переменная всегда или больше 0 или меньше 2048. Из-за этого размер какого-то блока не будет ограничен значением 512.


Правильный вариант кода:


if (ctx->block_size > 0 && ctx->block_size <= 2048) {  bs = ctx->block_size;} else {  bs = 512;}

Ошибка N35 N38; Переменная не изменяется


Предупреждение PVS-Studio: V547 Expression 'depth <= 0' is always false. panic_handler.c 169


static void print_backtrace(const void *f, int core){  XtExcFrame *frame = (XtExcFrame *) f;  int depth = 100;                                          // <=  //Initialize stk_frame with first frame of stack  esp_backtrace_frame_t stk_frame =    {.pc = frame->pc, .sp = frame->a1, .next_pc = frame->a0};  panic_print_str("\r\nBacktrace:");  print_backtrace_entry(esp_cpu_process_stack_pc(stk_frame.pc),                        stk_frame.sp);  //Check if first frame is valid  bool corrupted =    !(esp_stack_ptr_is_sane(stk_frame.sp) &&      (esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc)) ||       /* Ignore the first corrupted PC in case of InstrFetchProhibited */       frame->exccause == EXCCAUSE_INSTR_PROHIBITED));  //Account for stack frame that's already printed  uint32_t i = ((depth <= 0) ? INT32_MAX : depth) - 1;      // <=  ....}

Переменной depth присваивается значение 100, и до момента проверки этой переменной её значение нигде не изменяется. Это весьма подозрительно. Где-то что-то забыли сделать?


Аналогичные случаи:


  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. event_groups.c 260
  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. tasks.c 1475
  • V547 Expression 'xAlreadyYielded == ((BaseType_t) 0)' is always true. tasks.c 1520

Ошибка N39; Использование неинициализированного буфера


Предупреждение PVS-Studio: V614 Potentially uninitialized buffer 'k' used. Consider checking the second actual argument of the 'sae_derive_keys' function. sae.c 854


int sae_process_commit(struct sae_data *sae){  u8 k[SAE_MAX_PRIME_LEN];  if (sae->tmp == NULL ||      (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||      (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||      sae_derive_keys(sae, k) < 0)    return ESP_FAIL;  return ESP_OK;}

Ошибка в логике. Предположим, что указатели ec и dh являются нулевыми. В этом случае массив k не инициализируется, но функция sae_derive_keys всё равно начнёт его обрабатывать.


Ошибка N40; Всегда ложное условие


Предупреждение PVS-Studio: V547 Expression 'bit_len == 32' is always false. spi_flash_ll.h 371


static inline void spi_flash_ll_set_usr_address(spi_dev_t *dev, uint32_t addr,                                                int bit_len){  // The blank region should be all ones  if (bit_len >= 32) {    dev->addr = addr;    dev->slv_wr_status = UINT32_MAX;  } else {    uint32_t padding_ones = (bit_len == 32? 0 : UINT32_MAX >> bit_len);    dev->addr = (addr << (32 - bit_len)) | padding_ones;  }}

Как легко увидеть, условие bit_len == 32 всегда даст ложный результат. Возможно, выше следовало написать не больше-или-равно (>=), а просто больше (>).


Ошибка N41; Повторное присваивание


Предупреждение PVS-Studio: V519 The '* pad_num' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 46, 48. touch_sensor_hal.c 48


void touch_hal_get_wakeup_status(touch_pad_t *pad_num){  uint32_t touch_mask = 0;  touch_ll_read_trigger_status_mask(&touch_mask);  if (touch_mask == 0) {    *pad_num = -1;  }  *pad_num = (touch_pad_t)(__builtin_ffs(touch_mask) - 1);}

Код явно ошибочен и возможно здесь не хватает оператора else. Я не уверен, но, возможно, код должен выглядеть так:


void touch_hal_get_wakeup_status(touch_pad_t *pad_num){  uint32_t touch_mask = 0;  touch_ll_read_trigger_status_mask(&touch_mask);  if (touch_mask == 0) {    *pad_num = -1;  } else {    *pad_num = (touch_pad_t)(__builtin_ffs(touch_mask) - 1);  }}

Выход за границу массива


Ошибка N42; Неправильная граничная проверка


Предупреждение PVS-Studio: V557 Array overrun is possible. The value of 'frame->exccause' index could reach 16. gdbstub_xtensa.c 132


int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame){  const char exccause_to_signal[] =    {4, 31, 11, 11, 2, 6, 8, 0, 6, 7, 0, 0, 7, 7, 7, 7};  if (frame->exccause > sizeof(exccause_to_signal)) {    return 11;  }  return (int) exccause_to_signal[frame->exccause];}

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


if (frame->exccause >= sizeof(exccause_to_signal)) {

Ошибка N43; Длинный пример ошибки :)


В рассматриваемой функции выход за границу массива может произойти в двух местах, поэтому и предупреждений анализатора сразу два:


  • V557 Array overrun is possible. The value of 'other_if' index could reach 3. mdns.c 2206
  • V557 Array overrun is possible. The '_mdns_announce_pcb' function processes value '[0..3]'. Inspect the first argument. Check lines: 1674, 2213. mdns.c 1674

Приготовьтесь, это будет сложный случай. Для начала взглянем на следующие именованные константы:


typedef enum mdns_if_internal {    MDNS_IF_STA = 0,    MDNS_IF_AP = 1,    MDNS_IF_ETH = 2,    MDNS_IF_MAX} mdns_if_t;

Обратите внимание, что значение константы MDNS_IF_MAX равно 3.


Теперь взглянем на определение структуры mdns_server_s. Здесь нам важно, что массив interfaces состоит из 3 элементов:


typedef struct mdns_server_s {    struct {        mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];    } interfaces[MDNS_IF_MAX];    const char * hostname;    const char * instance;    mdns_srv_item_t * services;    SemaphoreHandle_t lock;    QueueHandle_t action_queue;    mdns_tx_packet_t * tx_queue_head;    mdns_search_once_t * search_once;    esp_timer_handle_t timer_handle;} mdns_server_t;mdns_server_t * _mdns_server = NULL;

Это ещё не всё. Нам понадобится заглянуть внутрь функции _mdns_get_other_if. Обратите внимание, что она может вернуть константу MDNS_IF_MAX. Т.е. она может вернуть значение 3.


static mdns_if_t _mdns_get_other_if (mdns_if_t tcpip_if){  if (tcpip_if == MDNS_IF_STA) {    return MDNS_IF_ETH;  } else if (tcpip_if == MDNS_IF_ETH) {     return MDNS_IF_STA;  }  return MDNS_IF_MAX;}

И вот, наконец, мы добрались до ошибок:


static void _mdns_dup_interface(mdns_if_t tcpip_if){    uint8_t i;    mdns_if_t other_if = _mdns_get_other_if (tcpip_if);    for (i=0; i<MDNS_IP_PROTOCOL_MAX; i++) {        if (_mdns_server->interfaces[other_if].pcbs[i].pcb) {        // <=            //stop this interface and mark as dup            if (_mdns_server->interfaces[tcpip_if].pcbs[i].pcb) {                _mdns_clear_pcb_tx_queue_head(tcpip_if, i);                _mdns_pcb_deinit(tcpip_if, i);            }            _mdns_server->interfaces[tcpip_if].pcbs[i].state = PCB_DUP;            _mdns_announce_pcb(other_if, i, NULL, 0, true);          // <=        }    }}

Итак, мы знаем, что функция _mdns_get_other_if может вернуть тройку. Значит переменная other_if может быть равна трём. И вот первый потенциальный выход за границу массива:


if (_mdns_server->interfaces[other_if].pcbs[i].pcb)

Второе место, где опасно используется переменная other_if, - это вызов функции _mdns_announce_pcb:


_mdns_announce_pcb(other_if, i, NULL, 0, true);

Заглянем в эту функцию:


static void _mdns_announce_pcb(mdns_if_t tcpip_if,                               mdns_ip_protocol_t ip_protocol,                               mdns_srv_item_t ** services,                               size_t len, bool include_ip){  mdns_pcb_t * _pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];  ....}

Опять может использоваться индекс 3 для доступа к массиву, состоящего из 3 элементов. А максимальный доступный индекс это двойка.


Нулевые указатели


Ошибка N44 N47; Ошибка очерёдности проверки указателей


Предупреждение PVS-Studio: V595 The 'hapd->wpa_auth' pointer was utilized before it was verified against nullptr. Check lines: 106, 113. esp_hostap.c 106


bool hostap_deinit(void *data){  struct hostapd_data *hapd = (struct hostapd_data *)data;  if (hapd == NULL) {    return true;  }  if (hapd->wpa_auth->wpa_ie != NULL) {    os_free(hapd->wpa_auth->wpa_ie);  }  if (hapd->wpa_auth->group != NULL) {    os_free(hapd->wpa_auth->group);  }  if (hapd->wpa_auth != NULL) {    os_free(hapd->wpa_auth);  }  ....}

Неправильная последовательность проверки указателей:


if (hapd->wpa_auth->group != NULL)....if (hapd->wpa_auth != NULL)

Если указатель hapd->wpa_auth окажется нулевым, то всё плохо. Последовательность действий нужно поменять местами и сделать вложенной:


if (hapd->wpa_auth != NULL){  ....  if (hapd->wpa_auth->group != NULL)  ....}

Аналогичные ошибки:


  • V595 The 'hapd->conf' pointer was utilized before it was verified against nullptr. Check lines: 118, 125. esp_hostap.c 118
  • V595 The 'sm' pointer was utilized before it was verified against nullptr. Check lines: 1637, 1647. esp_wps.c 1637
  • V595 The 'sm' pointer was utilized before it was verified against nullptr. Check lines: 1693, 1703. esp_wps.c 1693

Ошибка N48 N64; Нет проверки указателя после выделения памяти


В целом, в проекте принято проверять, удалось выделить память или нет. Т.е. много кода с подобными проверками:


dhcp_data = (struct dhcp *)malloc(sizeof(struct dhcp));if (dhcp_data == NULL) {  return ESP_ERR_NO_MEM;}

Но местами про проверки забыли.


Предупреждение PVS-Studio: V522 There might be dereferencing of a potential null pointer 'exp'. Check lines: 3470, 3469. argtable3.c 3470


TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags){  TRex *exp = (TRex *)malloc(sizeof(TRex));  exp->_eol = exp->_bol = NULL;  exp->_p = pattern;  ....}

Этот вид ошибки сложнее и опаснее, чем может показаться на первый взгляд. Подробнее эта тема разбирается в статье "Почему важно проверять, что вернула функция malloc".


Другие места, где отсутствуют проверки:


  • V522 There might be dereferencing of a potential null pointer 's_ledc_fade_rec[speed_mode][channel]'. Check lines: 668, 667. ledc.c 668
  • V522 There might be dereferencing of a potential null pointer 'environ'. Check lines: 108, 107. syscall_table.c 108
  • V522 There might be dereferencing of a potential null pointer 'it'. Check lines: 150, 149. partition.c 150
  • V522 There might be dereferencing of a potential null pointer 'eth'. Check lines: 167, 159. wpa_auth.c 167
  • V522 There might be dereferencing of a potential null pointer 'pt'. Check lines: 222, 219. crypto_mbedtls-ec.c 222
  • V522 There might be dereferencing of a potential null pointer 'attr'. Check lines: 88, 73. wps.c 88
  • V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 725, 724. coap_mbedtls.c 725
  • V575 The potential null pointer is passed into 'memset' function. Inspect the first argument. Check lines: 3504, 3503. argtable3.c 3504
  • V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 496, 495. mqtt_client.c 496
  • V575 The potential null pointer is passed into 'strcpy' function. Inspect the first argument. Check lines: 451, 450. transport_ws.c 451
  • V769 The 'buffer' pointer in the 'buffer + n' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 186, 181. cbortojson.c 186
  • V769 The 'buffer' pointer in the 'buffer + len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 212, 207. cbortojson.c 212
  • V769 The 'out' pointer in the 'out ++' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 233, 207. cbortojson.c 233
  • V769 The 'parser->m_bufferPtr' pointer in the expression equals nullptr. The resulting value of arithmetic operations on this pointer is senseless and it should not be used. xmlparse.c 2090
  • V769 The 'signature' pointer in the 'signature + curve->prime_len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 4112, 4110. dpp.c 4112
  • V769 The 'key' pointer in the 'key + 16' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 634, 628. eap_mschapv2.c 634

Ошибка N65, N66; Нет проверки указателя после выделения памяти (показательный случай)


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


Предупреждение PVS-Studio: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'exp->_nodes' is lost. Consider assigning realloc() to a temporary pointer. argtable3.c 3008


static int trex_newnode(TRex *exp, TRexNodeType type){  TRexNode n;  int newid;  n.type = type;  n.next = n.right = n.left = -1;  if(type == OP_EXPR)    n.right = exp->_nsubexpr++;  if(exp->_nallocated < (exp->_nsize + 1)) {    exp->_nallocated *= 2;    exp->_nodes = (TRexNode *)realloc(exp->_nodes,                                      exp->_nallocated * sizeof(TRexNode));  }  exp->_nodes[exp->_nsize++] = n; // NOLINT(clang-analyzer-unix.Malloc)  newid = exp->_nsize - 1;  return (int)newid;}

Во-первых, если функция realloc вернёт NULL, то будет потеряно предыдущее значение указателя exp->_nodes. Возникнет утечка памяти.


Во-вторых, если функция realloc вернёт NULL, то запись значения произойдёт вовсе не по нулевому указателю. Имеется в виду эта строка:


exp->_nodes[exp->_nsize++] = n;

Значение exp->_nsize++ может быть любым, и, если запись произойдёт в какую-то случайную область памяти, доступную для записи, то программа продолжит своё выполнение, как ни в чём не бывало. При этом будут разрушены структуры данных, что приведёт к непредсказуемым последствиям.


Ещё одна такая ошибка: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'm_context->pki_sni_entry_list' is lost. Consider assigning realloc() to a temporary pointer. coap_mbedtls.c 737


Прочие ошибки


Ошибка N67; Лишний или неверный код


Предупреждение PVS-Studio: V547 Expression 'ret != 0' is always false. sdio_slave.c 394


esp_err_t sdio_slave_start(void){  ....  critical_exit_recv();  ret = ESP_OK;  if (ret != ESP_OK) return ret;  sdio_slave_hal_set_ioready(context.hal, true);  return ESP_OK;}

Это странный код, который можно сократить до:


esp_err_t sdio_slave_start(void){  ....  critical_exit_recv();  sdio_slave_hal_set_ioready(context.hal, true);  return ESP_OK;}

Есть здесь ошибка или нет, мне сказать сложно. Возможно, здесь написано совсем не то, что задумано. А возможно, этот код появился в процессе неудачного рефакторинга и, на самом деле, является корректным. В этом случае его действительно достаточно немного упростить, чтобы он смотрелся красивей и понятней. Одно точно этот код заслуживает внимания и проверки автором.


Ошибка N68; Лишний или неверный код


Предупреждение PVS-Studio: V547 Expression 'err != 0' is always false. sdio_slave_hal.c 96


static esp_err_t sdio_ringbuf_send(....){  uint8_t* get_ptr = ....;  esp_err_t err = ESP_OK;  if (copy_callback) {    (*copy_callback)(get_ptr, arg);  }  if (err != ESP_OK) return err;  buf->write_ptr = get_ptr;  return ESP_OK;}

В общем-то, всё то же самое, что и в предыдущем случае. Переменная err является лишней или её забыли изменить.


Ошибка N69; Использование потенциально неинициализированного буфера


Предупреждение PVS-Studio: V614 Potentially uninitialized buffer 'seq' used. Consider checking the first actual argument of the 'strlen' function. linenoise.c 435


void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {    char seq[64];    if (hintsCallback && plen+l->len < l->cols) {        int color = -1, bold = 0;        char *hint = hintsCallback(l->buf,&color,&bold);        if (hint) {            int hintlen = strlen(hint);            int hintmaxlen = l->cols-(plen+l->len);            if (hintlen > hintmaxlen) hintlen = hintmaxlen;            if (bold == 1 && color == -1) color = 37;            if (color != -1 || bold != 0)                snprintf(seq,64,"\033[%d;%d;49m",bold,color);            abAppend(ab,seq,strlen(seq));                       // <=            abAppend(ab,hint,hintlen);            if (color != -1 || bold != 0)                abAppend(ab,"\033[0m",4);            /* Call the function to free the hint returned. */            if (freeHintsCallback) freeHintsCallback(hint);        }    }}

Буфер seq может быть заполнен, а может быть и не заполнен! Он заполняется только при выполнении условия:


if (color != -1 || bold != 0)  snprintf(seq,64,"\033[%d;%d;49m",bold,color);

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


Чтобы исправить ситуацию, стоит изменить код следующим образом:


if (color != -1 || bold != 0){  snprintf(seq,64,"\033[%d;%d;49m",bold,color);  abAppend(ab,seq,strlen(seq));}

Ошибка N70; Странная маска


Предупреждение PVS-Studio: V547 Expression is always false. tasks.c 896


#ifndef portPRIVILEGE_BIT  #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )#endifstatic void prvInitialiseNewTask(...., UBaseType_t uxPriority, ....){  StackType_t *pxTopOfStack;  UBaseType_t x;  #if (portNUM_PROCESSORS < 2)  xCoreID = 0;  #endif  #if( portUSING_MPU_WRAPPERS == 1 )    /* Should the task be created in privileged mode? */    BaseType_t xRunPrivileged;    if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )    {      xRunPrivileged = pdTRUE;    }    else    {      xRunPrivileged = pdFALSE;    }  ....}

Константа portPRIVILEGE_BIT имеет значение 0. Поэтому странно использовать его как маску:


if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )

Ошибка N71, Утечка памяти


Предупреждение PVS-Studio: V773 The function was exited without releasing the 'sm' pointer. A memory leak is possible. esp_wpa2.c 753


static int eap_peer_sm_init(void){  int ret = 0;  struct eap_sm *sm;  ....  sm = (struct eap_sm *)os_zalloc(sizeof(*sm));  if (sm == NULL) {    return ESP_ERR_NO_MEM;  }  s_wpa2_data_lock = xSemaphoreCreateRecursiveMutex();  if (!s_wpa2_data_lock) {    wpa_printf(MSG_ERROR, ".......");  // NOLINT(clang-analyzer-unix.Malloc)    return ESP_ERR_NO_MEM;             // <=  }  ....}

Если функция xSemaphoreCreateRecursiveMutex не сможет создать мьютекс, то функция eap_peer_sm_init завершит свою работу и при этом произойдёт утечка памяти. Как я понимаю, следует добавить вызов функции os_free для очистки памяти:


  s_wpa2_data_lock = xSemaphoreCreateRecursiveMutex();  if (!s_wpa2_data_lock) {    wpa_printf(MSG_ERROR, ".......");    os_free(sm);    return ESP_ERR_NO_MEM;  }

Что интересно, компилятор Clang тоже предупреждает об этой ошибке. Однако автор кода почему-то проигнорировал и даже специально подавил соответствующее предупреждение:


// NOLINT(clang-analyzer-unix.Malloc)

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


Заключение


Спасибо за внимание. Как видите, ошибок весьма много. А это ведь был только беглый просмотр неполного отчёта. Надеюсь, Юрий Попов примет эстафету и опишет ещё больше ошибок в своей последующей статье :).


Используйте статический анализатор PVS-Studio регулярно. Это позволит:


  1. Находить многие ошибки на раннем этапе, что существенно сократит расходы на их обнаружение и исправление;
  2. Находя и исправляя глупые опечатки и прочие ляпы с помощью статического анализа, вы высвободите время, которое можно потратить на более высокоуровневый обзор кода и алгоритмов;
  3. Лучше контролировать качество кода новичков и быстрее обучать их писать красивый надежный код;
  4. Если речь идёт о программном обеспечении для встраиваемых устройств, то очень важно устранить как можно больше ошибок до выпуска устройств в эксплуатацию. Поэтому любая дополнительно найденная ошибка с помощью анализатора кода, это здорово. Каждая незамеченная ошибка в программно-аппаратном устройстве потенциально несёт репутационные риски и затраты на обновление прошивок.

Приглашаю скачать и попробовать демонстрационную версию анализатора PVS-Studio. Также напоминаю, что если вы разрабатываете открытый проект или используете анализатор в академических целях, то для таких случаев мы предлагаем несколько вариантов бесплатных лицензий. Не ждите, когда коварный баг съест вашу ногу, начните использовать PVS-Studio прямо сейчас.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Espressif IoT Development Framework: 71 Shots in the Foot.

Подробнее..

ESP32 в окружении VSCode

30.11.2020 20:13:26 | Автор: admin

В нескольких следующих статьях я хотел бы детально рассмотреть настройку окружения VSCode для работы с фреймворком ESP-IDF. Не совсем популярная комбинация ПО обладает как преимуществами, так и недостатками, которые при детальном рассмотрении мы попытаемся исправить, обойти или превратить в достоинства.

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

Поскольку предполагается многопользовательская удаленная разработка, то мы решили вначале отработать выбор и настройку самой среды разработки. После нескольких экспериментов с Eclipse, Visual Studio и QT Creator выбор пал на кроссплатформенный VSCode и плагин от разработчика Espressif IDF для работы с фреймворком ESP-IDF.

В качестве сердца контроллера рассмотрим двухъядерный микроконтроллер ESP32, который обладает рядом преимуществ, которые планируется использовать и раскрыть в проекте, а именно:

  1. ESP32 это не MCU, а SoC*, имеющий на борту:

    1. Wi-Fi: 802.11 b/g/n/e/i (802.11n @ 2.4 GHz up to 150 Mbit/s)

    2. Bluetooth: v4.2 BR/EDR and Bluetooth Low Energy (BLE)

      * Все эти интерфейсы мы планируем использовать для коммуникации с изделием. А вот аналоговый радиоканал и FPV использовать по возможности не планируем.

  2. 2 Cores 240 MHz up to 600 DMIPS

  3. ULP co-processor

В качестве ядра ESP32 использует Tensilica Xtensa 32-bit LX6, у которого есть свои недостатки, главным из который, на мой взгляд, пока является слабая производительность с операциями с плавающей точкой в реальных приложениях. Возможно, это связано с текущей версией ядра LX6 или с компилятором GCC 8 toolchain, пока нет точного понимания. Пока это создает некоторые теоретические проблемы со скоростью работы фильтра Калмана и похожих алгоритмов (реализация Madgwick и Mahony).

Есть информация, что новое ядро LX7 уже имеет большую производительность, но оно пока используется только в одноядерной версии ESP32-S2.

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

Более детальное сравнение ядер Xtensa vs ARM можно посмотреть здесь.

Начиная с версии фреймворка ESP-IDF v4.0 Espressif существенно улучшила техническую поддержку и дальнейшую разработку фреймворка. Качественно улучшилась техническая документация и поддержка разработчиков на форуме компании и GitHub. Кардинально улучшилась оперативность обработки сообщений об ошибках в фреймворке и их устранение. Ускорился выпуск новых версий базового фреймворка ESP-IDF, и дополнительных на нем основанных, например Arduino-ESP32.

НАСТРОЙКА ОКРУЖЕНИЯ

Установку всех приложений и утилит мы будем рассматривать на совершенно чистую ОС (Win10Pro). Поэтому, если у вас уже установлены какие-то из перечисленных ниже приложений или утилит, то я рекомендую их удалить. Или учитывайте далее, что параллельное использование нескольких копий или версий программ из окружения может привести к непредсказуемым результатам, отличным от описанных ниже, и делайте советующие поправки на результат.

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

В состав окружения будут входить, в порядке очередности установки:

  1. Git

  2. Python

  3. CMake

  4. VSCode ESP-IDF (далее фреймворк)

  5. ESP-IDF Tools (далее Toolchain)

Я рекомендую все утилиты ставить в отдельную папку в корень диска, например в C:\dev , за исключением VSCode.

А проекты сохранять в папку C:\dev\esp32 .

В дальнейшем это сделает настройку фреймворка более удобной, с визуальной точки зрения, поскольку существенно сократит длину путей к утилитам Toolchain.

Git

Сначала поставим Git

Download URL: https://git-scm.com/

Installation path: C:\dev\Git

Python

Затем поставим Python

Download URL: https://www.python.org/downloads/

Installation path: C:\dev\Python39

Перед установкой убедитесь, что в папке C:\Users\UserName\AppData\Roaming\Python\Python39 не осталось скриптов от предыдущих установок. Почему скрипты для Python из Toolchain попадают в C:\Users\UserName\AppData\Roaming\Python\Python39 (внутри есть еще две папки \Scripts и \site-packages) для меня загадка. Правда скрипты попадают в эту папку только если производить установку ESP-IDF Toolchain из дистрибутива esp-idf-tools-setup-2.3.exe или более ранних версий. Буду признателен, если кто-то подскажет ответ.

(Q1) Вопрос 1: Почему скрипты для Python из дистрибутива ESP-IDF Tools попадают в папку C:\Users\UserName\AppData\Roaming\Python\Python39 (\Scripts и \site-packages), а не в папку с Python? Можно ли изменить этот путь на пользовательский?

Вообще буду признателен за комментарии и ответы на заданные по ходу статьи вопросы. В случае ответа на вопросы прошу начинать их в комментариях с префикса (Q1), где цифра после Q номер вопроса, так их будет легче обрабатывать по тексту.

CMake

Теперь поставим CMake

Download URL: https://cmake.org/download/

Installation path: C:\dev\CMake

На текущий момент Toolchain ESP-IDF использует для сборки проекта только свою версию CMake v3.13.4, которая идет в комплекте, и категорически не хочет использовать другие, в отличии от Python. Это выглядит странно, поскольку на сколько мне известно, у версий 3.13-19 нет явных проблем с обратной совместимостью.

(Q2) Вопрос 2: Какие проблемы с обратной совместимостью есть у CMake версий 3.13-19? Почему Toolchain ESP-IDF не позволяет использовать альтернативные версии CMake?

VSCode

Теперь можно установить VSCode

Download URL: https://code.visualstudio.com/

Installation path: в папку по умолчанию

VSCode plugins

Далее необходимо поставить в VSCode два плагина:

  • Espressif IDF (espressif.esp-idf-extension)

  • C/C++ IntelliSense (ms-vscode.cpptools)

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

Default Terminal Shell

Перед тем как продолжить, я настоятельно рекомендую установить CMD как оболочку терминала VSCode по умолчанию. Дело в том, что по умолчанию после установки в VSCode в качестве терминала используется MS PowerShell, но не все скрипты, которые используются в плагине Espressif IDF для работы с фреймворком корректно выполняются в powershell. А в новой версии оболочки PowerShell, которую предлагается скачать и установить, эти скрипты выполняются еще хуже. К тому же далее мы будем использовать в настройках ESP-IDF некоторый лайфхак, который в PowerShell вообще не выполняется.

Для установки оболочки терминала по умолчанию запустите терминал Terminal ==> New Terminal и выберете окне терминала в выпадающем списке опцию Select Default Shell. Далее выберите в списке Command Prompt cmd.exe

Окно терминала теперь можно закрыть.

Установка ESP-IDF

Если вы еще не убирали опцию Show Onboarding on Visual Studio Code start в настройках Espressif IDF, то после перезагрузки VSCode и выбора в правой части экрана иконки с логотипом Espressif автоматически запустится мастер настройки ESP-IDF (далее просто Мастер).

ESSPRESSIF

Данный Мастер можно также вызвать командой ESP-IDF: Configure ESP-IDF extension (onboarding.start)

Теперь можно приступить непосредственно к установке и базовой конфигурации фреймворка и Toolchain.

Для начала выберем, где хранить настройки фреймворка.

Для первого раза я рекомендую сохранить настройки непосредственно в пустой папке нового проекта, которую мы предварительно создадим, например здесь: C:\dev\esp32\device01 , и откроем в VSCode. Это немного облегчит понимание, какие настройки, когда и для чего создает данный Мастер, и какие настройки понадобятся еще.

Для сохранения настроек в нашу папку выберем опцию Workspace folder settings и укажем путь C:\dev\esp32\device01.

Нажимаем START

_ User & Workplace

Небольшое отступление.

VSCode оперирует двумя типами настроек глобальными, которые обозначаются как User и локальными, которые обозначаются как Workspace и действуют для конкретного проекта. Есть некоторое несоответствие английских названий смыслу, поскольку User ассоциируется с пользовательскими настройками, но так сложилось исторически, и этот момент нужно просто запомнить, как есть. Мы будем далее называть настройки из раздела User Глобальными, а из Workspace Локальными. Если кто-то знает более подходящую терминологию, милости прошу поделиться в комментариях.

Локальные настройки применяются после Глобальных и имеют над ними приоритет, т.е. при дублировании настроек применяются Локальные.

Настройки в VSCode можно просматривать как просто текстовые файлы JSON, так и в форме графического интерфейса VSCode. Полезной фитчей последнего является то, что в разделе измененные значения параметров относительно значений по умолчанию подкрашиваются синей полосой. Это упрощает навигацию по параметрам и также позволяет в два клика вернуться к дефолтным значениям.

Таким образом, те настройки, которые мы получим в результате работы Мастера и сохраним в папке проекта будут для нас Локальными (Workspace), а в дальнейшем мы перенесем их и в Глобальные (User).

Select Python version to use

На данном экране необходимо указать путь к вашей версии Python. Его можно выбрать из выпадающего списка. Иногда скрипты Мастера дают сбой, и после выбора версии Python из списка предлагается снова ввести путь к исполняемому файлу python.exe вручную. Тут ничего нельзя поделать, придется повторить путь вручную.

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

Проверяем путь к Python и нажимаем Configure ESP-IDF

Configure ESP-IDF

На данном экране необходимо выбрать версию фреймворка ESP-IDF, с которой мы хотим работать (и которая будет далее скачиваться), и указать путь, где фреймворк будет сохранен.

Можно также выбрать опцию Find ESP-IDF in your system и указать папку с ранее скаченным фреймворком. Именно этой опцией, скорее всего, вы будете пользоваться в дальнейшей повседневной работе.

Если вы хотите дополнительно использовать в своих разработках класс Arduino, как компоненту проекта, то необходимо выбрать версию ESP-IDF - release/v4.0 (release branch) или v4.0.2 (release version), т.к. фреймворк Arduino-ESP32 доступен сейчас только для версии v3.3, v4.0 и v4.2.

Однако, v3.3 уже морально устарела, а v.4.2, на мой взгляд, пока еще слишком сырая и не имеет окончательного релиза, хотя к моменту окончания нашей установки она может уже и стабилизироваться.

Использование более ранних версий фреймворков ESP-IDF и Arduino-ESP32 чем v.4.0 настоятельно не рекомендуется. Чтобы убедиться в этом можно просто ознакомиться с количеством изменений для базового фреймворка v4.0 по сравнению с версиями v3.x https://github.com/espressif/esp-idf/releases/tag/v4.0 , а фреймворк Arduino-ESP32 основывается как раз на базовой версии фреймворка ESP-IDF. Версии ниже v4.0 также плохо поддерживают, или вообще не поддерживают CMake, а все наши дальнейшие изыскания будут связаны именно с этой популярной системой сборки проектов.

Примечательно, что для Arduino IDE доступен только фреймворк Arduino-ESP32 на версии v.3.3 ESP-IDF, поэтому разработка в нашем окружении VSCode дает несомненное преимущество при работе с классом Arduino, как с компонентой проекта. Правда в этой связке тоже не все так гладко, о чем мы позднее поговорим более подробно.

Если вы хотите на этапе разработки проекта получать поменьше обновлений для вашего фреймворка, то необходимо выбирать версию (release version), а не (release branch).

А мы тем временем, выбираем release/v4.0 (release branch), а в качестве пути к фреймворку ESP-IDF укажем папку C:\dev , к которой автоматически добавится адрес \esp-idf .

Нажимаем Check here to download, после чего начинается процесс загрузки с GitHub.

Ждем несколько минут и после окончания загрузки внезапно видим ошибку:

Дело в том, что в плагине Espressif IDF версии 0.5.1 вот уже много месяцев присутствует ошибка для выбора release/v4.0 (release branch) после загрузки репозитария v4.0 система пытается найти архив для версии 4.0.2

Будем надеяться, что в будущих версиях эту ошибку исправят. Соответствующую проблему я зарегистрировал на GitHub https://github.com/espressif/vscode-esp-idf-extension/issues/223

А нам остается только выбрать v4.0.2 (release version) и повторить загрузку.

После успешной загрузки и распаковки фреймворка Мастер предложит перейти к настройке утилит Toolchain.

Нажимаем Go to ESP-IDF Tools setup

ESP-IDF Tools Configuration

На данном экране выбираем Download ESP-IDF Tools

Вторая опция выбирается если у вас уже есть папка с предварительно скаченным или клонированным с GitHub фреймворком.

Теперь необходимо указать папку, в которую будет загружен Toolchain ESP-IDF Tools. Укажем C:\dev\.espressif .

Обратите внимание, ниже перечислены версии всех утилит, входящих в комплект Toolchain для ESP-IDF v4.0.2 именно с ними будет гарантироваться работа (компиляция) как самого фреймворка ESP-IDF, так и компоненты Arduino-ESP32 v4.0.

Tool: xtensa-esp32-elf Version: esp-2020r3-8.4.0

Tool: esp32ulp-elf Version: 2.28.51.20170517

Tool: cmake Version: 3.13.4

Tool: openocd-esp32 Version: v0.10.0-esp32-20200709

Tool: mconf Version: v4.6.0.0-idf-20190628

Tool: ninja Version: 1.9.0

Tool: idf-exe Version: 1.0.1

Tool: ccache Version: 3.7

Нажимаем Download

Ждем несколько минут и в самом конце установки получаем в консоли ошибку:


ERROR: Failed building wheel for psutil

ERROR: Command errored out with exit status 1:

VisualStudio is not installed; get it from http://www.visualstudio.com/en-au/news/vs2015-preview-vs

error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": https://visualstudio.microsoft.com/downloads/

...

WARNING: You are using pip version 20.2.3; however, version 20.2.4 is available.

You should consider upgrading via the 'C:\dev\.espressif\python_env\idf4.0_py3.9_env\Scripts\python.exe -m pip install --upgrade pip' command.


Оказывается, для установки одной из компонент wheel for psutil требуется ее предварительно скомпилировать с участием MS С++14 и именно из состава Visual Studio! Ну кто бы мог подумать?!

Я не стал пока искать альтернативных решений, а просто перешел по предложенной ссылке https://visualstudio.microsoft.com/downloads/ , скачал и установил VisualStudio.

После чего вернулся в окно VSCode и снова нажал Download.

Забегая вперед, скажу, что в этот раз установка ESP-IDF Tools закончилась успешно, все компоненты скомпилировались и установились. Однако мне осталось не ясным, нужен ли для успешной установки именно компилятор С++14 и состава Visual Studio, или подойдет любой другой компилятор С++14, установленный в системе?

Если у вас не было данной ошибки, значит в вашей системе стояли необходимые компиляторы. Буду признателен, если вы поделитесь описанием вашей конфигурации. С какими компиляторами установка ESP-IDF Tools завершилась у вас удачно?

(Q3) Вопрос 3: С какими компиляторами С++14, помимо штатного из состава Visual Studio, может успешно завершиться установка ESP-IDF Tools? Нужно ли обязательно при этом иметь в системе установленную программу Visual Studio, или достаточно только компилятора?

_ PIP

Перед тем, как вы тоже повторите действия выше с установкой Visual Studio и нажмете Download обратите внимание на последние строчки сообщения об ошибки. Там сказано, что в системе используется устаревшая версия pip в то время, как доступна более новая.

PIP это система управления пакетами, которая используется для установки и управления программными пакетами, написанными на Python. На сколько я помню, pip попадает в систему автоматически при установке Python версии > 3.4.

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

Для этого запустим командную стоку CMD из меню Пуск и введем сначала команду C:\dev\.espressif\python_env\idf4.0_py3.9_env\Scripts\python.exe -m pip install --upgrade pip , а затем python -m pip install --upgrade pip

Дело в том, что первая команда обновит pip в локальной копии idf4.0_py3.9_env, а вторая команда обновит pip уже в основной системной версии Python.

_ PIP cache

Еще один нюанс кроется в кэше pip. Если после всех установок и настроек ESP-IDF вы вдруг решите удалить Visual Studio со всеми дистрибутивами C++, а потом решите повторно запустить мастер настройки ESP-IDF, то процесс установки ESP-IDF Tools пройдет успешно, как ни в чем не бывало. Дело в том, что в процессе предыдущей установки pip сохранил в кэш результаты всех успешных компиляций. Кэш находится по адресу C:\Users\UserName\AppData\Local\pip\cache , и далее, при повторных установках, если в кэше находится файл для утилиты подходящей версии, то он берется именно из кэша. Новые файлы будут компилироваться только для новых версий утилит из Toolchain.

Для того чтобы провести полную переустановку Toolchain для ESP-IDF, без использования кэша pip, достаточно просто удалить папку C:\Users\UserName\AppData\Local\pip\cache\wheels , это вернет в консоль сообщение о недостающем дистрибутиве С++14, если таковой отсутствует в вашей системе.

Теперь скачиваем Visual Studio https://visualstudio.microsoft.com/downloads/ , устанавливаем его с одной единственной опцией Разработка классических приложений на C++, возвращаемся в окно VSCode и

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

Возвращаемся на шаг назад, нажав на стрелку влево над надписью ESP-IDF Tools, нажимаем Download ESP-IDF Tools и снова нажимаем Download.

В случае успешной установки Toolchain станет доступна кнопка Go to next step.

В логе ниже мы увидим снова предупреждение, что используется устаревшая версия pip. Да как так-то?..

Дело в том, что если мы отмотаем лог наверх, то найдем сообщение:

Creating a new Python environment in C:\dev\.espressif\python_env\idf4.0_py3.9_env ...

Да, Мастер создал новую версию локального окружения Python, в которую поместил версию pip, идущую в комплекте с Toolchain, а не ту, которая установлена и обновлена в системном Python. Почему Мастер не взял системную версию pip неизвестно. Будем надеяться, что в будущих версиях Матера это поправят.

Чтобы снова обновить локальную версию pip снова выполняем в командной строке команду C:\dev\.espressif\python_env\idf4.0_py3.9_env\Scripts\python.exe -m pip install --upgrade pip

Нажимаем Go to next step

Verify ESP-IDF Tools

Теперь мастер предлагает нам проверить все абсолютные пути для утилит из Toolchain.

Обратите внимание, мастер сделал локальную копию нашего Python39 со всеми скриптами и окружением - idf4.0_py3.9_env, и менять этот путь не стоит.

Также учтите, что несмотря на установленный в системе CMake v3.19 ESP-IDF использует свой CMake v3.13.4, и это доставит нам в дальнейшем некоторые сложности. Заменить путь к CMake более новой версии на данном этапе пока не удастся, т. к. на следующем шаге проверки Мастер укажет на несоответствие версий и не даст завершить настройку. Поэтому оставляем все как есть.

Нажимаем Click here to check tools exist

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

Если все соответствует везде появятся положительные галочки и сообщения are satisfied.

Нажимаем Go to next step

ESP-IDF Tools have been configured

И вот она, заветная надпись об успешном окончании установки.

Настройки проекта

Однако не спешите пока открывать примеры кода. Давайте посмотрим вначале, что же записал мастер настройки ESP-IDF в нашу папу, которую мы указали в самом начале как C:\dev\esp32\device01

В данной папке появилась папка .vscode с единственным файлом settings.json который содержит всего пять строк:

{

"idf.espIdfPathWin": "C:\\dev\\esp-idf",

"idf.toolsPathWin": "C:\\dev\\.espressif",

"idf.customExtraPaths": "C:\\dev\\.espressif\\python_env ",

"idf.customExtraVars": "{\"OPENOCD_SCRIPTS ",

"idf.pythonBinPathWin": "C:\\dev\\.espressif\\python_env "

}

Это пять основных переменных, которые определяют какой именно фреймворк ESP-IDF и Toolchain будут применяться для компилирования и работы с проектом.

Но это еще не все настройки, которые нам необходимы.

Теперь давайте создадим еще одну папку, например C:\dev\esp32\device02 , откроем ее в VSCode и выполним команду ESP-IDF: Add vscode configuration folder, далее будем для краткости называть ее ESP:Avcf. Для выполнения команды нажмите F1 и начните набирать ESP, далее уже можно выбрать команду из списка.

Теперь в папке .vscode появилось уже 4 файла. Причем в фале settings.json нет переменных, которые мы получили после мастера настройки ESP-IDF. А в фале c_cpp_properties.json как раз есть ссылки на эти переменные с путями к фреймворку и Toolchain.

Более того, если бы мы выполнили команду ESP:Avcf в нашей первой папке device01, то наши настройки были бы перезаписаны новым файлом settings.json .

Все дело в том, что, к сожалению, эти две команды работают не совсем взаимосвязано. Мастер настройки ESP-IDF запоминает в переменных среды idf пути к фреймворку и toolchain и сохраняет их, в нашем случае, Локально. А команда ESP:Avcf создает конфигурационные файлы ВСЕГДА используя Глобальные переменные.

Теперь получается, для того чтобы корректно заработали настройки в c_cpp_properties.json, нам необходимо сохранить наши переменные Глобально? И да, и нет.

Дело в том, что, как мы уже говорили ранее, параметры Workspace (Локальные) применяются после параметров User (Глобальных), и если и там и там есть одни и те же параметры, то Workspace имеет преимущество перед User и перезаписывает их. Таким образом, мы может как внести наши переменные в User, так и добавить их в наш Workspace, после параметров, созданных командой ESP:Avcf, в низ файла settings.json, непосредственно в папку .vscode в нашей папке проекта . И то и другое будет работоспособно.

Однако, если вы ведете совместную разработку проекта, например на GitHub, то вам я рекомендую обязательно внести все настройки в файл settings.json в самом проекте, чтобы гарантировать себе, что проект будет собираться в одном и том же фреймворке и Toolchain у всех участников разработки.

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

Внести настройки переменных idf в User можно как вручную, так и еще раз запустив Мастер, выбрав в самом начале опцию User settings и далее указав папку уже со скаченным фреймворком ESP-IDF.

Но в начале давайте откроем один из примеров кода и попробуем скомпилировать его с Локальными настройками.

ПРИМЕР КОДА и КОМПИЛЯЦИЯ

Чтобы открыть библиотеку примеров кодов ESP-IDF выполним команду ESP-IDF: Show ESP-IDF Examples Projects.

В открывшемся списке выберем проект get-started\blink и нажмем кнопку Crate project using example blink. Разместим проект в папке esp32.

Теперь откроем файл .vscode\settings.json и добавим в конец файла наши настройки из папки device01. Будьте внимательны с синтаксисом JSON, не забудьте про запятые и фигурные скобки.

Теперь перейдем на фаqл с кодом проекта main\blink.c .

Обратите внимание, после открытия проект содержит две проблемы - BLINK_GPIO и portTICK_PERIOD_MS подкрашены красным системой проверки синтаксиса C/C++ IntelliSense. Мы разберем этот момент чуть позже, следующей статье про тонкие настройки окружения VSCode.

А сейчас выполним команду ESP-IDF: Build your project.

Первый процесс компиляции занимает несколько минут, поскольку создается папка build, последующие будут происходить значительно быстрее. В конце компиляции появится сообщение Build Successfully. Это значит, наши Локальные настройки применились успешно.

К сожалению, в случае компиляции проекта командой ESP-IDF: Build your project не происходит отображения лога компиляции в окне терминала. Но можно выполнить эту команду другим способом, из меню Terminal => Run Task => Build Build project или Terminal => Run Build Task

Если окно терминала не было открыто, оно откроется автоматически и в конце компиляции в терминале появится сообщение Project build complete. To flash, run this command:..

Выполнение команд из меню Terminal обеспечивается скриптами в файле .vscode\task.json , но их ход выполнения немного отличается от аналогичных команд через меню F1, например ESP-IDF: Build your project.

На этом мы пока прервемся мы смогли сделать базовую настройку плагина Espressif IDF для работы с VSCode и успешно скомпилировали один из примеров кода из библиотеки фреймворка.

В следующей статье мы займемся более тонкой настройкой окружения VSCode и самого фреймворка рассмотрим утилиту menuconfig, новые скрипты для task.json и решим проблему с не всегда корректной подсветкой синтаксиса IntelliSense, также рассмотрим выполнение команд фреймворка из командной строки.

Подробнее..

ESP32-C3 первое знакомство. Заменим ESP8266?

12.02.2021 18:18:50 | Автор: admin

Новая игрушка

В ноябре 2020 года Espressif анонсировала новую SoC под названием ESP32-C3. Они разослали несколько инженерных прототипов для тестирования и первого ознакомления.

Так довелось, что мне попался один из них на руки и я успел немного поиграться с ним. Надеюсь, мой краткий опыт будет интересен сообществу, так как тут есть на что посмотреть и ESP32-C3 имеет хороший шанс получить свою нишу в мире встраиваемых систем, так и в области DIY. Меня как раз больше интересует подход со стороны DIY, поэтому статья будет больше с этим уклоном.

В чём разница?

ESP32-C3 пытается занять нишу между ESP32/ESP32-S и нашим старым другом ESP8266. Даже больше хочет вытеснить ESP8266, чем быть дополнением к линейке ESP32.

Не знаю что там будет с ценами, но переход на RISC-V, упрощение периферии и прочее может сделать ESP32-C3 достаточно привлекательным, что бы он смог заменить ESP8266 полностью. В итоге мы получим достойную замену старичку, с которым мы вместе с 2014 года.

В таблице ниже я привёл основные отличия ESP32-C3 от предыдущих ESP32 и ESP32-S2. Так же у Espressif недавно был анонсирован ESP32-S3 с двумя ядрами, но я пока не буду его приводить ниже в силу отсутствия его в широкой продаже.

ESP32

ESP32-S2

ESP32-C3

CPU

Xtensa LX6

Xtensa LX7

RISC-V (RV32IMC)

Частота CPU

160 / 240 MHz

160 / 240 MHz

160 MHz

Количество ядер CPU

2 / 1

1

1

ULP CPU

ULP-FSM

ULP-RISC-V

ULP-FSM

-

SRAM

520 KB

320 KB

400 KB

RTC SRAM

16 KB

8 KB

8 KB

WiFi

802.11bgn

150 Mbit

802.11bgn, 802.11mc

150 Mbit

802.11 bgn, 802.11mc

150 Mbit

Bluetooth

4.2 BR/EDR BLE

-

Bluetooth 5

GPIO

34

43

22

12-bit ADC

1

18 каналов

2

20 каналов

2

6 каналов

8-bit DAC

2

2

-

Touch sensor

10

14

-

SPI

4

4

3

I2S

2

1

1

I2C

2

2

1

UART

3

2

2

SDIO

1

-

-

Ethernet MAC

1

-

-

PWM

16

8

6

USB OTG

-

1

-

Потребление

До 240 мА

До 310 мА

До 325 мА

Modem sleep

27 - 68 мА

12 - 19 мА

15 - 20 мА

Light sleep

0,8 мА

0,45 мА

0,13 мА

Deep sleep

0,01 - 0,15 мА

0,02 - 0,19 мА

0,005 мА

Корпус

48 выводов

56 выводов

32 вывода

Как ESP8266, но не все выводы такие же

DevKit плата

ESP32-C3 приехал ко мне в виде платы ESP32-C3-DevKitM-1, на которой установлен модуль ESP32-C3-Mini-1 со встроенной 4 МБ флеш-памятью.

Так же на этой плате стоит USB-Serial конвертер CP2102 для подключения и прошивки по USB. На GPIO подключен RGB светодиод WS2812.

Сам модуль ESP32-C3-Mini-1 физически по размеру заметно меньше того же ESP-12E на базе ESP8266. И в то же время ESP32-C3 имеет намного больше возможностей.

"Engineering Sample Notes"

С платой была одностраничное приложение с некоторыми пометками.

Например, там написано, что потребление на DevKit'е ещё не достаточно оптимизировано и потому не рекомендуется для оценки в Deep-sleep режиме.

Так же сказано, что в данной версии чипа поддержка USB Serial/JTAG отсутствует, но она будет присутствовать в финальной версии.

Текущая версия ESP-IDF в процесс работы по добавлению поддержки ESP32-C3. Работа в этом направлении ведётся в ветках "master" и "release/v4.3".

Поддержка ESP-IDF

ESP-IDF является официальным фреймворком для разработки под ESP32. Сама среда поддерживает всю линейку ESP32. Большинство примеров можно собрать под Xtensa LX6/LX7, так и под RISC-V. Переключение сводится к одной команде "idf.py set-target esp32c3", которая выставляет riscv32-esp-elf- и прочие параметры в sdkconfig. Теперь после компиляции у нас готова прошивка для нового ESP32-C3.

Предварительно надо подготовить окружение в зависимости от ОС: Windows, Linux или macOS.

git clone --recursive https://github.com/espressif/esp-idf.gitcd esp-idf# Linux/macOS./install.sh# Windowsinstall.ps1

Установочный скрипт скачает компиляторы и дополнительные пакеты для ESP-IDF. После этого надо импортировать окружение для начала работы:

# Linux/macOS. ./export.sh# Windowsexport.bat

Рекомендую для проверки собрать "hello world":

cd examples/get-started/blink# По-умолчанию настроено для ESP32, но если надо собрать прошивку для ESP32-S2idf.py set-target esp32s2# Или для ESP32-C3idf.py set-target esp32c3# Сборка, прошивка, USB консольidf.py build flash monitor

Не все примеры собираются под ESP32-C3, но Espressif активно работает над ESP-IDF для поддержки этого чипа.

CoreMark

Ради интереса запустил CoreMark бенчмарк для оценки производительности нового RISC-V ядра и сравнил эти данные с предыдущим ESP32 (к сожалению, у меня нет ESP32-S2 для более широкого сравнения).

Ниже приведены попугаи для разной частоты процессора и для общего ознакомления добавлены значения при компиляции с выключенными оптимизациями (-O0):

CoreMark 1.0

GCC

Частота CPU

ESP32-C3

388

GCC8.4.0 -O3

160 МГц

98

GCC8.4.0 -O0

160 МГц

ESP32 (одно ядро)

313

GCC8.4.0 -O3

160 МГц

77

GCC8.4.0 -O0

160 МГц

469

GCC8.4.0 -O3

240 МГц

115

GCC8.4.0 -O0

240 МГц

Я сделал измерения ESP32-C3 на 80 МГц, но значения получились ровно в два раза ниже, что и предполагалось и поэтому в таблице не привожу.

В попугаях RISC-V получился примерно на 24% быстрее. Конечно, обычные задачи, которыми мы загружаем такие микроконтроллеры будут сильно отличаться от синтетических бенчмарков, но было интересно посмотреть.

Надо так же не забывать, что ESP32 поддерживает работу на частоте 240 МГц, а у ESP32-C3 максимальная частота только 160 МГц. Так же ESP32 имеет два ядра, которые можно задействовать в зависимости от задач.

Но повышенная частота 240 МГц так же скажется на потреблении устройства. То есть, ESP32-C3 за каждый попугай будет просить меньше электронов (я не измерял потребление, но есть несколько замеров других наблюдателей).

ESP RainMaker

Вместе с этой новой платой Espressif продвигает платформу ESP RainMaker. Это некая среда, которая позволяет быстро создавать концепты и прототипы для IoT устройств.

На ESP32-C3-DevKitM-1 плате уже была прошивка ESP RainMaker, с которой можно получить общее представление об этом платформе.

Идея примерно такова:

На плате настроен обычный бинарный переключатель. При подключении платы через USB консоль мы получаем QR код, который надо отсканировать в приложении ESP RainMaker (доступно для Android и iOS) для первоначальной конфигурации настроек WiFi и добавлении этого устройства в приложение.

После этого можно управлять встроенным RGB светодиодом на плате через Espressif облако, которое находится на Amazon AWS.

ESP RainMaker SDK позволяет создавать разнообразные устройства, не только бинарный переключатель как в примере. Это может быть датчик измерения температуры или влажности, LCD экран, несколько выключателей сразу или много разных устройств вместе сразу. В последнем случае одна плата будет показана как несколько устройств в приложении.

Для того, что бы поиграться с ESP RainMaker не обязательно ждать когда можно купить ESP32-C3. Сама платформа замечательно работает на ESP32 и ESP32-S2. Для сборки прошивки достаточно иметь настроенный ESP-IDF:

git clone --recursive https://github.com/espressif/esp-rainmaker.gitcd esp-rainmaker/examples/switch# По-умолчанию настроено для ESP32, но если надо собрать прошивку для ESP32-S2idf.py set-target esp32s2# Или для ESP32-C3idf.py set-target esp32c3# Сборка, прошивка, USB консольidf.py build flash monitor

В консоле появится QR код и дальше уже всё в приложении.

Выводы

Большее количество микроконтроллеров для фанатов DIY всегда позитивно, так как при большем выборе можно подобрать более подходящий МК и плату в зависимости от того, что более важно в новом проекте.

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

Отдельное спасибо Espressif за то, что сделали WiFi доступным для встраиваемых устройств 7 лет назад. А там и другие производители подтягиваются. Мы только в плюсе.

Подробнее..

Делаем систему контроля и управления доступом (СКУД) для умного дома

20.02.2021 20:08:41 | Автор: admin

Введение

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

Как это работает: на входной двери размещён датчик открытия, который по протоколу Zigbee сообщает серверу умного дома, что кто-то зашёл в квартиру. Срабатывает сигнализация в "тихом режиме" (событие "triggered" во встроенной интеграции; это никак не проявляется, но идёт обратный отсчёт до запуска сирены). Если за указанное в настройках время не снять блокировку (через ввод кода или NFC-меткой), запустится сирена и световая индикация.

Из чего собрано:

  • ESP32 WROOM DevKit v1 (в теории можно заменить любой ESP, изменив конфиг под неё)

  • RFID/NFC модуль PN532

  • Соединительные провода (6 штук)

  • Напечатанный на 3D-принтере корпус

  • Xiaomi Gateway 2 (который с локальным управлением) я планирую использовать как динамик и световую индикацию

  • Датчик открытия двери от Aqara

  • Опционально можно добавить люстру, LED-ленты, умные колонки и любые другие устройства на ваш вкус, цвет и возможности автоматизаций Home Assistant.

ESP32 WROOM DevKit v1 (30 контактов)ESP32 WROOM DevKit v1 (30 контактов)RFID/NFC модуль PN532. Китайцы скопировали версию от Elechouse.RFID/NFC модуль PN532. Китайцы скопировали версию от Elechouse.

Корпус мне намечал друг, у которого есть 3D-принтер. Хаб и датчики от Xiaomi вынесем за скобки. Остальные элементы покупались на Aliexpress и суммарно обошлись мне в 600 рублей.

Подключение и настройка ESP

Для начала переключим NFC-модуль в режим работы через интерфейс SPI. Ставим первый переключатель в нижнее положение (ближе к цифре 1), а второй - в верхнее (ближе к буквам). Припаиваем гребёнку на 8 контактов и готовим соединительные провода.

Включенный режим I2C и подключенные соединительные проводаВключенный режим I2C и подключенные соединительные провода

Насколько я понял из распиновки, возможно несколько вариантов подключения NFC-модуля к ESP-32: мне было удобнее подключить всё на одну сторону. Если будете использовать другие контакты, внимательно проверяйте конфиг - возможно, он немного изменится.

Распиновка для 30-контактной ESP-32Распиновка для 30-контактной ESP-32

Подключаем модуль следующим образом (слева ESP, справа PN532):

  • GPIO18 - SKC

  • GPIO19 - MSO

  • GPIO23 - MOSI

  • GPIO5 - SS

  • 3V3 - VCC

  • GND - GND

PN532 подключенная к ESP-32PN532 подключенная к ESP-32

На следующем этапе нам нужно установить аддон ESPHome и настроить нашу ESP-32. Подробно расписывать базовые моменты не буду, рекомендую следовать данному видео:

Остановлюсь лишь на итоговом конфиге:

esphome:  name: esp32  platform: ESP32  board: nodemcu-32swifi:  ssid: "My Wi-Fi"  password: "mypassword"  # Enable fallback hotspot (captive portal) in case wifi connection fails  ap:    ssid: "Esp32 Fallback Hotspot"    password: "mypassword"captive_portal:# Enable logginglogger:# Enable Home Assistant APIapi:  password: "mypassword"ota:  password: "mypassword"web_server:  port: 80spi:  clk_pin: GPIO18  miso_pin: GPIO19  mosi_pin: GPIO23pn532_spi:  cs_pin: GPIO5  update_interval: 1sesp32_ble_tracker:switch:  - platform: gpio    name: "ESP LED"    pin:      number: GPIO2      mode: OUTPUTbinary_sensor:  - platform: pn532    uid: 79-EB-08-B4    name: "NFC Card"

Обратите внимание на блоки spi и pn532_spi, где мы указывает контакты подключения. В блоке switch я задействовал светодиод на плате (им можно мигать, например, при поднесении валидной метки), а в блоке binary_sensor создал сущность для Home Assistant (при поднесении карты с указанным uid сенсор переходит в статус true; uid карты можно найти в логах вашей ESP в аддоне ESPHome). Как показали опыты, можно читать RFID-метки, банковские карты и тройку. NFC в моём телефоне нет, но скорее всего и он будет работать.

Компилируем прошивку и выгружаем её на ESP. Проверяем, что всё работает, открыв логи и поднеся к считывателю RFID-метку. Её uid должен отобразиться в логе:

[17:42:35][D][pn532:149]: Found new tag '79-EB-08-B4'

Со стороны ESP всё готово, теперь нужно настроить автоматизации в Home Assistan

Подключение сигнализации в Home Assistant

Для работы в сигнализацией в Home Assistant есть встроенная интеграция и карточка Lovelace. Начнём с интеграции - чтобы её включить, нужно добавить в configuration.yaml следующий блок:

alarm_control_panel:  - platform: manual    code: !secret alarm_pin    code_arm_required: false    # Задержка перед постановкой на охрану    arming_time: 5    # Задержка перед запуском сигнализации    delay_time: 10    # Время сигнализации    trigger_time: 600

Код для разблокировки я вынес в отдельный файл secrets.yaml. Почитать, как он устроен, можно тут.

Поскольку мы тестируем нашу СКУД, arming_time (время до включения режима охраны, за которое вы успеете выйти из квартиры и закрыть дверь) и delay_time (время после срабатывания датчика двери, через которое запустится сирена) зададим как 5 и 10 секунд соответственно. Сохраняем, перезагружаем Home Assistant.

Далее создаём карточку сигнализации в Lovelace, добавив код в нужное вам место ui-lovelace.yaml

- type: alarm-panel  name: Сигнализация  entity: alarm_control_panel.ha_alarm  states:    - arm_away

В entity указываем название объекта, который создался после подключения alarm_control_panel. В states можно указать, какие кнопки будут в карточке: я оставил только "Охрана (не дома)".

Автоматизация

Чтобы связать NFC-метки с нашим умным домом, потребуется создать 5 автоматизаций:

  • Срабатывание сигнализации (запускается, когда мы заходим в квартиру)

  • Включение режима охраны (прикладываем метку и уходим из дома)

  • Отключение режима охраны (прикладываем метку, когда пришли домой)

  • Включение сирены

  • Отключение сирены

Срабатывание сигнализации

В качестве триггера используется датчик открытия двери. Когда дверь открывается при условии, что включен режим охраны, запускается наша автоматизация. В блоке с действиями я задал мигание шлюзом Xiaomi и диодом на ESP-32. Вы можете использовать любые другие действия.

- id: '3-0001'  alias: 'Срабатывание сигнализации'  trigger:    platform: state    entity_id: binary_sensor.158d000446f3fe_contact    to: 'on'  condition:    - condition: state      entity_id: alarm_control_panel.ha_alarm      state: armed_away  action:  - service: alarm_control_panel.alarm_trigger    entity_id: alarm_control_panel.ha_alarm  - repeat:        sequence:          - service: light.turn_on            data:                entity_id: light.gateway_light_44237c82f751                color_name: red                brightness: 255          - service: switch.turn_on            entity_id: switch.esp_led          - delay:                milliseconds: 500          - service: light.turn_off            entity_id: light.gateway_light_44237c82f751          - service: switch.turn_off            entity_id: switch.esp_led          - delay:                milliseconds: 500        until:          condition: or          conditions:          - condition: state            entity_id: alarm_control_panel.ha_alarm            state: armed_away          - condition: state            entity_id: alarm_control_panel.ha_alarm            state: disarmed

Включение режима охраны

Триггер - чтение метки с заданным на этапе настройки ESP uid. Пока мы выходим из квартиры, шлюз мигает оранжевым светом. После того, как включился режим охраны, загорается диод на ESP, а шлюз включает статичный красный свет на 3 секунды и гаснет.

- id: '3-0002'  alias: 'Включение режима охраны'  trigger:    platform: state    entity_id: binary_sensor.nfc_card    to: 'on'  condition:    - condition: state      entity_id: alarm_control_panel.ha_alarm      state: disarmed  action:  - service: alarm_control_panel.alarm_arm_away    entity_id: alarm_control_panel.ha_alarm  - repeat:        sequence:          - service: light.turn_on            data:                entity_id: light.gateway_light_44237c82f751                color_name: orange                brightness: 255          - service: switch.turn_on            entity_id: switch.esp_led          - delay:                milliseconds: 500          - service: light.turn_off            entity_id: light.gateway_light_44237c82f751          - service: switch.turn_off            entity_id: switch.esp_led          - delay:                milliseconds: 500        until:          - condition: state            entity_id: alarm_control_panel.ha_alarm            state: armed_away  - service: switch.turn_on    entity_id: switch.esp_led  - service: light.turn_on    data:        entity_id: light.gateway_light_44237c82f751        color_name: red        brightness: 255  - delay:        seconds: 3  - service: light.turn_off    entity_id: light.gateway_light_44237c82f751

Отключение режима охраны

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

- id: '3-0003'  alias: 'Отключение режима охраны'  trigger:    platform: state    entity_id: binary_sensor.nfc_card    to: 'on'  condition:    condition: or    conditions:    - condition: state      entity_id: alarm_control_panel.ha_alarm      state: armed_away    - condition: state      entity_id: alarm_control_panel.ha_alarm      state: pending  action:  - service: alarm_control_panel.alarm_disarm    data:        entity_id: alarm_control_panel.ha_alarm        code: !secret alarm_pin  - delay:        milliseconds: 100  - service: switch.turn_off    entity_id: switch.esp_led  - service: light.turn_on    data:        entity_id: light.gateway_light_44237c82f751        color_name: green        brightness: 255  - delay:        seconds: 3  - service: light.turn_off    entity_id: light.gateway_light_44237c82f751

Включение сирены

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

- id: '3-0004'  alias: 'Запуск звука сигнализации'  trigger:  - platform: state    entity_id: alarm_control_panel.ha_alarm    to: 'triggered'  action:  - service: xiaomi_aqara.play_ringtone    data:        gw_mac: 44237C82F751        ringtone_id: 0        ringtone_vol: 3

Отключение сирены

От "отключения режима охраны" отличается лишь условием по статусу alarm_control_panel.ha_alarm (здесь triggered) и отключением сирены или другой индикации.

- id: '3-0005'  alias: 'Отключение сигнализации'  trigger:    platform: state    entity_id: binary_sensor.nfc_card    to: 'on'  condition:    - condition: state      entity_id: alarm_control_panel.ha_alarm      state: triggered  action:  - service: alarm_control_panel.alarm_disarm    data:        entity_id: alarm_control_panel.ha_alarm        code: !secret alarm_pin  - service: xiaomi_aqara.stop_ringtone    data:        gw_mac: 44237C82F751  - delay:        milliseconds: 500  - service: switch.turn_off    entity_id: switch.esp_led  - service: light.turn_on    data:        entity_id: light.gateway_light_44237c82f751        color_name: green        brightness: 255  - delay:        seconds: 3  - service: light.turn_off    entity_id: light.gateway_light_44237c82f751

Красивая обёртка

Наверное, очевидно, что две платы без какого-либо корпуса выглядят не очень красиво и безопасно. Я попросил другая спроектировать и напечатать под них корпус. Цвет выбрали белый, чтобы подходил под будущий интерьер. Уже после печати я понял, что белый корпус не гасит свет диодов на ESP, поэтому их можно использовать в автоматизациях. Даже синий диод в условиях коридора должен быть виден.

Основа, на которую ложатся платы, и прищепка (слева) для того, чтобы закрепить PN532Основа, на которую ложатся платы, и прищепка (слева) для того, чтобы закрепить PN532К ESP подключается кабель питания, поэтому она не должна болтаться внутри корпуса.К ESP подключается кабель питания, поэтому она не должна болтаться внутри корпуса.

Ух, наконец-то закончил. Спасибо, что дочитали до конца. Надеюсь, что этот гайд помог вам!

Если возникнут какие-то вопросы, задавайте в комментариях. Постараюсь ответить.

Подробнее..

Поговорим с мышами? Или Soft USB HOST на Esp32

08.03.2021 22:21:25 | Автор: admin

Esp32 весьма мощный контроллер, подходящий для эмуляции различных ретро систем, таких как Spectrum, Commodore, NES, IBM PC-XT и тд. Есть возможность сгенегировать VGA или AV - TV композитный сигнал, подключить различные компактные LCD дисплеи. Он умеет разговаривать с SD картами по SPI & SD протоколу. Вот только с USB клавиатурами, мышами и джойстиками - не умеет. Попробуем научить его говорить с ними. Есть конечно новый вариант ESP32-S3 с одним USB host контроллером, а мне нужно подключить хотя бы 3 девайса и без хаба...

Нам понадобится (ссылки только для примера) :

  1. Собственно сам ESP32 ~3$ WEMOS LOLIN32

  2. Несколько разъемов USB ~3$ за десяток

  3. Мне понадобился логический анализатор ~$3 CY7C68013A и бесплатная отличная программа анализатора протоколов

  4. Спецификации USB и USB-HID (HID - Human Interface Device).

  5. Макетные платы или любовь к паяльнику/припою/канифоли.

USB 1.1

Большинство HID умеет разговаривать с хостом этого стандарта. Имеет две скорости работы HS (High Speed) -12 Mbits/s и LS (Low speed)- 1.5 Mbits/s . К сожалению, скорости процессора ногодрыгом (Generic Pin IO) недостаточно для работы в HS, поэтому мы будем рассматривать реализацию только LS (1.5 Mbits/s). Впрочем большая часть HID devices работает именно на этой скорости. На физическом уровне USB 1.1 - имеет два сигнальных провода: D+ и D- , провод земли : GRN и провод питания: VBus. Сигналы в D+ и D- измеряются относительно провода GRN, хотя большую часть времени они работают как дифф-пара (для уменьшения синфазных помех, наводок) , то есть в противофазе. Шина (D+ и D-) может принимать 4 состояния (high - ~3.3v low - 0v ) для LS :

'K' : D+ - high D- low

'J' : D+ - low D- high

SE0: D+ - low D- low

SE1: D+ - high D- high

Состояние SE1 - незаконное. В исправной системе шина в этом состоянии находиться не должна

Когда к шине хоста ничего не подключено , по стандарту, D+ и D- присоединены со стороны хоста к земле через резисторы 15 kOhm (у нас получается около 50 kOhm - это величина pull-down резисторов в ESP32 чипе, впрочем работе это не мешает). При подключении LS девайса он замыкает провод D- на 3.3v через резистор в 5 kOhm, а HS девайс - провод D+, что позволяет отличить HS девайс от LS.

Определив на шине появление LS девайса , после задержки в 200 миллисекуд (дать время девайсу закончить переходные процессы) мы начинаем с ним разговаривать. В протоколе USB инициатором всегда является хост (в отличии от протокола PS/2 например). Раз в миллисекунду хост посылает сообщение девайсу и тот отвечает. Данные передаются младшим битом вперед, 0 кодируется переходом 'K' в 'J' или 'J' в 'K'. Единица - отсутствием перехода. Если встречается больше 6 единиц подряд , то принудительно вставляется переход состояния 'K' в 'J' или 'J' в 'K' . (NRZI coding) . Передача идет пакетами, начало пакета - синхронизирующая последовательность "KJKJKJKK" ('00000001' binary или число 80Н , младший бит передается первым ), конец пакета "SE0,SE0, J". первый байт пакета (после синхронизирующей последовательности ) тип пакета (packet ID - PID). Остальные байты и их кол-во - зависят от типа пакета. Пакеты можно поделить на короткие (handshake) ACK, NACK, STALL ... из одного байта , средние IN,OUT,SETUP из 3 байт и длинные DATA0,DATA1 из 3-11 байт. Формат короткого пакета 8 bit start, 8 bit PID, SE0,SE0, J.

Формат среднего пакета: 8 bit start, 8 bit PID, 7 bit address (назначенный адрес устройства), 4 bit EP (конечная точка), 5 bit CRC5 (контрольная сумма адреса и устройства) .

Формат длинного пакета 8 bit start, 8 bit PID (DATA0 или DATA1) 0-8 байт данных и 2 байта crc16 (только от данных).

После физического подключения устройства хост должен:

  1. запросить тип устройства (диск ,камера, HID...)

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

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

  4. выбрать нужную конфигурацию

  5. дальше один раз в миллисекунду:

  6. послать запрос на конечную точку и получить или данные или NACK

  7. послать пустой пакет (SE0,SE0, J) на устройство если не пришло время запрашивать данные.

Если пришли данные - проверить пакет на корректность (чек сумма и тип) и послать ACK если все верно или запросить данные повторно. К сожалению некоторые устройства не дают достаточно времени для проверки корректности пакета (не могу на CPU одновременно и принимать и посчитывать), поэтому я сначала не отвечаю на пакет , проверяю его его корректность , запрашиваю его повторно на следующей миллисекунде и только тогда отвечаю устройству ACK. Такое поведение , в целом, соответствует спецификации (если устройство соответствует спецификации USB 1.1 LS, то это должно работать).

Примерный код приемника - узкое место (должен успевать делать заметно больше двух оборотов на бит и функция _getCycleCount8d8() - младшие 8 бит абсолютного времени = цикл CPU/8 (240MHz/8 = 30 MHz)- время на 6 бит должно быть меньше 256 (в байт)):

while(act>0 && (val||nval) ) // есть активность и  не нули на шине{val  = nval;nval = READ_BOTH_PINS;  //прочитать порт  и наложить маску пинов D+  D-,пины в старшем байтеreceived_NRZI_buffer[locRec] = _getCycleCount8d8() |  nval;  // записать время и состояние в 16 битif(val!=nval) // была замечена активность{locRec++;act = TOUT;   // была замечена активность,отложить выход из цикла на TOUT}else act--;  // }

Примерный код передатчика:

SET_O;  // cконфигурировать пины на выход// в transmit_NRZI_buffer состояния на передачу 0 - 'K'  1 - 'J  2 - se0for(k=0;k<transmit_NRZI_buffer_cnt;k++){cpuDelay(TRANSMIT_TIME_DELAY);  // задержка передачи одного бита  *snd[transmit_NRZI_buffer[k]][0] = DM_PIN_M;*snd[transmit_NRZI_buffer[k]][1] = DP_PIN_M;   }SET_I; // cконфигурировать пины на вход
Pulseview работа с тремя мышами
Набор в работе
Детям после 16.
Слева направо: логический анализатор, ESP32, STM32 обслуживающий себя сам Слева направо: логический анализатор, ESP32, STM32 обслуживающий себя сам

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

sourcе на 4 HID устройства.

Подробнее..

Через тернии к звёздам или LILYGO TTGO T-Internet-POE ESP32 LAN8270A

15.03.2021 10:06:02 | Автор: admin
image
Попалась мне на глаза плата LILYGO TTGO T-Internet-POE ESP32 LAN8270A и конечно я не мог пройти мимо такой интересной новинки: ESP32, LAN8270A, POE, SD карта, Wi-Fi+Ethernet Было интересно пощупать это произведение сумрачного китайского гения своими руками и протестировать в реальной работе, ведь TTX платы сулили очень интересные перспективы для использования в IoT, DIY и вообще в области Wi-Fi+Ethernet и на что фантазии хватит.

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

Камень в огород LILYGO


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

Хорошим примером тут может служить LILYGO TTGO T-Internet-POE ESP32 LAN8270A (далее для краткости будем называть эту плату T-Internet-POE). Производитель сделал интересную плату, но не сделал больше ничего:

  • нет нормальной распиновки контроллера
  • нет принципиальной электрической схемы
  • нет вменяемого описания применения платы и типовых паттернов её использования
  • нет технических пояснений по работе отдельных составляющих платы
  • нет примеров кода (есть 1 (!) скетч на отвали, мальчик, не мешай работать)
  • нет сайта с документацией
  • нет форума с грамотными и мотивированными модераторами
  • нет популярных и мотивирующих статей для тех, кто интересуется этим контроллером, но не знает (не понимает) где он мог бы его применить для своих целей
  • и нет ещё множества вещей, которые должны были бы быть

Короче, нет вообще ничего и каждый, кто рискнёт купить T-Internet-POE, должен быть безупречным воином DIY, иначе у него нет ни одного шанса выстоять в этой битве с LILYGO. Много ли среди нас таких?

И как при таком подходе к делу они вообще умудряются что-то продавать? И насколько выросли бы их продажи, если бы они на минутку отложили в сторону паяльник и вспомнили о своих покупателях?

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

В чём фишка?


Говоря простыми словами, на этой плате удалось более-менее удачно объединить в одном устройстве ESP32 (Wi-Fi), Ethernet, POE и ещё добавить к этому торту вишенку в виде microSD картридера. Из одного только сочетания этих составляющих сразу вытекает множество интересных вариантов применения этой платы:

  • работа по Wi-Fi с резервом в виде Ethernet канала
  • работа по Ethernet с резервом в виде Wi-Fi подключения
  • обслуживание одновременно и Wi-Fi и Ethernet линков
  • роутер между Wi-Fi и Ethernet в обе стороны
  • веб-сервер на два интерфейса
  • разные веб-сервера на разных интерфейсах
  • питание (удалённого) контроллера по POE

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

Как вы видите, сфера применения этой платы в IoT и DIY ограничена только вашей фантазией и вашими потребностями и в целом T-Internet-POE как устройство смотрится очень многообещающе.

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

Технические характеристики


Мы здесь не будем приводить полный список всех технических характеристик платы вот с чем с чем, а с этим на сайтах продавцов и производителя проблем нет (проблема в том, что кроме этих характеристик там больше ничего нет). Приведём здесь только список ключевых элементов платы:

  • ESP32-WROOM (4 МБ)
  • LAN8720A (Ethernet PHY)
  • POE 802.3af
  • microSD картридер
  • 12 GPIO пинов для внешних подключений

О чём навскидку нам говорит такая конфигурация? О том, что при использовании этой платы в качестве веб-сервера, файлы можно хранить как на microSD карте памяти, так и во внутренней памяти модуля ESP32 (или и там и там сразу).

При этом 12 свободных GPIO производят двойственное впечатление с одной стороны это уже кое-что и значительно лучше чем на ESP8266, а с другой стороны после проектов на Mega 2560 с её десятками GPIO, 12 пинов смотрятся очень и очень скромно и сильно ограничивают возможности разработки тут нужно будет либо изобретать какие-то расширители портов, либо делать тандемные сборки с другими контроллерами.

Варианты контроллера


При ближайшем рассмотрении оказывается, что под одним названием существуют два разных контроллера один на ESP32-WROOM, а второй на ESP32-WROVER-B, что сходу и не разглядишь на вид платы практически одинаковые и можно играть в игру найди 10 отличий.

image

Мне достался контролер на ESP32-WROOM, поэтому дальнейшее повествование будет относится к нему.

Программатор


Инженеры LILYGO так далеко оторвались от своих пользователей, что их решения не всегда можно понять. К таким решениям относится создание ими отдельной платы программатора на чипе CP2104 для контроллера T-Internet-POE.

Зачем? Зачем нужен отдельный программатор, когда этот узел можно было интегрировать на саму плату контроллера или попросту использовать стандартные USB-TTL переходники (как делают все остальные производители железа)? Ответ, видимо, знают только разработчики LILYGO (но простим им этот маленький креатив).

image

Но мало того, что разработчики LILYGO сделали непонятную вещь, они ещё и умудрились сделать её криво:

  • во-первых они применили горячо любимые народом пины с шагом 2,0 мм
  • во-вторых они предусмотрели установку разъёма на обратную (!) сторону платы

(Разработчикам такого решения я бы порекомендовал иногда отвлекаться от работы и уделять время отдыху.)

В результате получился какой-то странный монстр. И всё бы ничего, если бы проблема ограничивалась только эстетической составляющей, но тут вылезают более серьёзные проблемы:

  • если устанавливать и крепить плату в нормальном положении, то пины программатора оказываются снизу платы и к ним невозможно подключиться без демонтажа контроллера;
  • если работать с платой без крепления то разъёмы с шагом 2,0 не обеспечивают должной жёсткости и вся конструкция грозит развалиться в любой момент и всё вокруг позамыкать.

В качестве нивелирования этого косяка разработчиков, можно порекомендовать припаивать линейку пинов с верхней стороны платы и сделать (или купить, если он продаётся) 6-пиновый 2,0 мм гибкий переходник.

Распиновка


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

image

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

Всего на ESP32 имеется 40 пинов (D0D39) из них 14 пинов

D6D11, D20, D24, D28-D31, D37, D38

мы исключаем из рассмотрения как практически неиспользуемые (по разным причинам, подробный разбор назначения этих пинов выходит за рамки этой статьи). Остаются:

Пины подключения Ethernet чипа LAN8720A


D18 ETH_MDIO_PIN
D19 ETH_TX_D0
D21 ETH_TX_EN
D22 ETH_TX_D1
D23 ETH_MDC_PIN
D25 ETH_RX_D0
D26 ETH_RX_D1
D27 ETH_RX_CRS

причём, D18 и D23 устанавливаются в скетче, а остальные 6 пинов чипа LAN8720A являются стандартными и задаются в библиотеке.

Поскольку производитель постеснялся предоставить принципиальную схему контроллера, то я здесь могу только привести аналогичную типовую схему подключения физики Ethernet на LAN8720A.

image

К LAN8720A также относится пин тактирования, который на плате T-Internet-POE подключён к D17 (тоже выбирается в скетче):

D17 ETH_CLOCK

и пин сброса

D5 NRST

microSD картридер


microSD картридер подключён на HSPI:

D2 SD_MISO
D12 SD_CS
D14 SD_SCLK
D15 SD_MOSI

и в случае своего использования забирает свободные для внешних подключений и выведенные на плату пины D2, D14, D15. Вопрос что выгоднее использовать картридер и потерять 3 из 12-и свободных пинов или сохранить 3 лишних GPIO и отказаться от картридера сродни вопросу что лучше: слон или конь? и вам каждый раз придётся отвечать на него при использовании платы T-Internet-POE.

Прочие пины


У нас остаются пины D0 (ETH_CLOCK, не задействован в этом качестве) и D1 (TX0) и D3 (RX0).

Свободные пины


Теперь переходим к самому интересному описанию свободных пинов, выведенных на плату контроллера.

Первой идёт группа D34, D35, D36, D39, работающая только на вход. Лучше конечно на вход, чем вообще ничего, но при таком дефиците GPIO было бы гораздо лучше, если бы эти четыре пина были полноценными GPIO.

И затем 8 полноценных GPIO, которые вы можете использовать в своих проектах. Тут нужно помнить, что хоть эти пины и являются полноценными GPIO, но некоторые из них работают весьма своеобразно (например меняют потенциал на старте контроллера и т.п.). Поэтому прежде, чем к ним что-то подключать, нужно специально выяснять и уточнять эти моменты.

D2 (SD_MISO)
D4
D12
D14 (SD_SCLK)
D15 (SD_MOSI)
D16
D32
D33

Как говорится, вот тебе, мой юный друг программирования и микроконтроллеров, 8 GPIO и ни в чём себе не отказывай.

POE


Тут нужно ещё сказать несколько слов о поддержке POE, поскольку это является одним из достоинств этой платы и одной из её фишек и для многих это будет причиной, по которой они захотят её купить и использовать.

Здесь реализована полноценная поддержка стандарта POE 802.3af с развязкой и управлением питанием на чипе SI3404.

Меня дистанционная запитка контроллера не очень интересует, поэтому этот аспект работоспособности T-Internet-POE я не тестировал, но, судя по всему, с POE здесь проблем нет.

Программная поддержка


Как вы сами понимаете, работать с T-Internet-POE можно при помощи любых программных сред, имеющих представление об этом железе, в том числе в нативном SDK (и вероятно это наиболее правильный вариант), но мы попытаемся выяснить, что можно выжать из этой железки при помощи Arduino IDE.

В качестве программной среды для экспериментов использовались Arduino IDE версии 1.8.5 и ESP32-Arduino версии 1.0.5 (последняя сборка на момент написания статьи).

Сам процесс установки поддержки ESP32 в Arduino IDE я описывать не буду потому, что этому вопросу посвящено огромное количество материалов в интернете, во всех нюансах описывающих этот процесс.

Упомяну здесь только один момент: плюс ко всему, чего не имеет этот контроллер, он ещё не имеет и нативной поддержки в ESP32-Arduino версии 1.0.5. Поэтому в качестве контроллера в менеджере плат выбирался ESP32 DEV Module с настройками:

Flash Mode: DIO
Flash Size: 4MB (32 Mb)
Partition Scheme: 4MB (1,2MB/1,5MB)

Стандартный скетч


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

Полный код скетча от производителя платы
/*    This sketch shows how to configure different external or internal clock sources for the Ethernet PHY*/#include <ETH.h>#include <SPI.h>#include <SD.h>#define SD_MISO         2#define SD_MOSI         15#define SD_SCLK         14#define SD_CS           13/*   * ETH_CLOCK_GPIO0_IN   - default: external clock from crystal oscillator   * ETH_CLOCK_GPIO0_OUT  - 50MHz clock from internal APLL output on GPIO0 - possibly an inverter is needed for LAN8720   * ETH_CLOCK_GPIO16_OUT - 50MHz clock from internal APLL output on GPIO16 - possibly an inverter is needed for LAN8720   * ETH_CLOCK_GPIO17_OUT - 50MHz clock from internal APLL inverted output on GPIO17 - tested with LAN8720*/// #define ETH_CLK_MODE    ETH_CLOCK_GPIO0_OUT          // Version with PSRAM#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT            // Version with not PSRAM// Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)#define ETH_POWER_PIN   -1// Type of the Ethernet PHY (LAN8720 or TLK110)#define ETH_TYPE        ETH_PHY_LAN8720// IC-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)#define ETH_ADDR        0// Pin# of the IC clock signal for the Ethernet PHY#define ETH_MDC_PIN     23// Pin# of the IC IO signal for the Ethernet PHY#define ETH_MDIO_PIN    18#define NRST            5static bool eth_connected = false;void WiFiEvent(WiFiEvent_t event){    switch (event) {    case SYSTEM_EVENT_ETH_START:        Serial.println("ETH Started");        //set eth hostname here        ETH.setHostname("esp32-ethernet");        break;    case SYSTEM_EVENT_ETH_CONNECTED:        Serial.println("ETH Connected");        break;    case SYSTEM_EVENT_ETH_GOT_IP:        Serial.print("ETH MAC: ");        Serial.print(ETH.macAddress());        Serial.print(", IPv4: ");        Serial.print(ETH.localIP());        if (ETH.fullDuplex()) {            Serial.print(", FULL_DUPLEX");        }        Serial.print(", ");        Serial.print(ETH.linkSpeed());        Serial.println("Mbps");        eth_connected = true;        break;    case SYSTEM_EVENT_ETH_DISCONNECTED:        Serial.println("ETH Disconnected");        eth_connected = false;        break;    case SYSTEM_EVENT_ETH_STOP:        Serial.println("ETH Stopped");        eth_connected = false;        break;    default:        break;    }}void testClient(const char *host, uint16_t port){    Serial.print("\nconnecting to ");    Serial.println(host);    WiFiClient client;    if (!client.connect(host, port)) {        Serial.println("connection failed");        return;    }    client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);    while (client.connected() && !client.available());    while (client.available()) {        Serial.write(client.read());    }    Serial.println("closing connection\n");    client.stop();}void setup(){    Serial.begin(115200);    WiFi.onEvent(WiFiEvent);    SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);    if (!SD.begin(SD_CS)) {        Serial.println("SDCard MOUNT FAIL");    } else {        uint32_t cardSize = SD.cardSize() / (1024 * 1024);        String str = "SDCard Size: " + String(cardSize) + "MB";        Serial.println(str);    }    pinMode(NRST, OUTPUT);    digitalWrite(NRST, 0);    delay(200);    digitalWrite(NRST, 1);    delay(200);    digitalWrite(NRST, 0);    delay(200);    digitalWrite(NRST, 1);    ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);}void loop(){    if (eth_connected) {        testClient("baidu.com", 80);    }    delay(10000);}


Когда я в первый раз увидел этот скетч, то задался вопросом: что этим хотел сказать производитель?. Единственное назначение этого скетча показать, что эта технология в принципе работает. Это конечно хорошо, но что дальше? Что со всем этим делать не имея ни документации, ни примеров, ни вменяемой ответной реакции от производителя?

Походу получается, что ответ от LILYGO изучать программирование и создавать ПО самостоятельно (или искать готовые прошивки, хотя это и не спортивно).

Интерфейсы и пинг


Для сетевых профессионалов (и примкнувших к ним) скажу пару слов о скорости работы по интерфейсам Wi-Fi и Ethernet и их отзывчивости. Тестирование проводилось в ненагруженной гигабитной сети, зашумлённость Wi-Fi диапазона специально не контролировалась.

Первый скриншот это пинг контроллера по Wi-Fi интерфейсу. Минимум 24 мс, максимум 105 мс, среднее 67 мс.

image

Второй пинг контроллера по Ethernet интерфейсу. Минимум 0 мс, максимум 9 мс, среднее 2 мс.

image

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

Тестирование


Тестировать такую систему, как T-Internet-POE на скетчах, подобных предложенному производителем это несерьёзно, поэтому для тестирования контроллера применялась специализированная версия AMS, адаптированная специально для этой платы. Учитывая, что это сервер, который использует полноценные HTML, CSS, Javascript, Ajax, графические файлы и библиотеки, то успешная работа такого сервера на T-Internet-POE будет свидетельствовать о правильно спроектированном железе и возможности его использования в реальных проектах.

Примечание: тестирование производилось на внутренней, не публичной версии AMS для T-Internet-POE. Публикация и распространение этой версии не планируется, возможно это будет сделано позже, после соответствующих доработок.

Тест 1. Запускаем AMS сервер на T-Internet-POE


Запустить AMS на новом контроллере с новым чипом и сетевым интерфейсом это весьма нетривиальная задача, но тем не менее, при правильном подходе и понимании того, что ты делаешь, всё становится возможным.

Косяк номер 1


В процессе этой работы стали вылезать косяки самого контроллера T-Internet-POE и первое, что было выявлено это то, что контроллер отказывается прошиваться при вставленной microSD карте памяти. Не помогает ничего ни замена USB порта, ни питание от отдельного блока, ни нажимание кнопок, ни замена карты контроллер упорно не желает прошиваться при вставленной карте памяти.

Глюк это конкретного экземпляра или родовой дефект всех контроллеров T-Internet-POE сказать трудно (имея в своём распоряжении один экземпляр), можно только констатировать 100-процентную повторяемость и воспроизводимость проблемы.

Что это значит для нас? В практическом плане это значит, что на контроллере T-Internet-POE фактически нет картридера картридер, который блокирует прошивку контроллера это не картридер, а баг.

Что же делать? Остаётся только использовать 1,5 МБ SPIFFS, имеющийся на модуле ESP32. Да, это не очень много, но в принципе 1,5 МБ памяти для IoT устройства это более-менее приемлемо в большинстве случаев.

Косяк номер 2


Ок, от картридера мы отказались, теперь нам нужно подружиться с SPIFFS. Вроде бы задача несложная и даже привычная, но и тут нас ждёт засада: по какой-то причине утилита ESP32FS отказывается нормально работать на этом контроллере (в этой конфигурации). Перенос файлов в память модуля ESP32 приводит к последующей ошибке монтирования SPIFFS диска.

М-да В случае невозможности нормально перенести файлы (сервера) на SPIFFS диск, остаётся только один способ инициализация интерфейса через Serial соединение, и последующий перенос файлов на SPIFFS диск через веб-интерфейс. Способ конечно не очень удобный, но никак не влияющий на конечный результат файлы сервера были успешно перенесены на SPIFFS диск.

Описание самого процесса адаптации кода под новый контроллер я опускаю, поскольку это потребовало бы составления антологии наподобие полного собрания сочинений В. И. Ленина и сразу перехожу к демонстрации факта успешной работы AMS сервера на T-Internet-POE (а значит и работоспособности самой платы T-Internet-POE).

Загрузка страницы по Wi-Fi интерфейсу.

image

Загрузка страницы по Ethernet интерфейсу.

image

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

image

Работа сервера по Ethernet интерфейсу на LILYGO TTGO T-Internet-POE ESP32 LAN8270A.

Тест 2. Работа на двух интерфейсах


Тут мне придётся немного поработать разрушителем легенд. В интернете ходят упорные слухи, что одновременная работа Wi-Fi и Ethernet на связке ESP32 и LAN8270A невозможна. Это не так AMS сервер прекрасно работает на двух интерфейсах одновременно и отлично обслуживает запросы по Wi-Fi и Ethernet. Никаких проблем с зависаниями или перезагрузками ESP32 нет.

image

Вот это уже интересный результат, который открывает заманчивые перспективы: поскольку мы имеем собственный сервер, то можем как угодно управлять обслуживанием интерфейсов, например, по Wi-Fi отдавать одни сайты с одним контентом, а по Ethernet другие сайты с другим контентом. Образно говоря, бабушке по Ethernet отдавать сайт с кулинарными рецептами, а внуку по Wi-Fi сайт с избранными статьями из БСЭ.

Тест 3. Бан по одному из интерфейсов


Теперь давайте попробуем на практике реализовать идею с различным обслуживанием интерфейсов веб-сервером. В качестве примера попробуем реализовать отказ в обслуживании для подключений по одному из интерфейсов (Ethernet).

image

Клиент, подключённый к нашему контроллеру по Ethernet интерфейсу получил отказ в обслуживании.

Резервирование интерфейсов


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

Сетевой роутинг


Имея в своём распоряжении два рабочих сетевых интерфейса можно как угодно маршрутизировать пакеты в сети. Никто также не мешает в схему маршрутизации по Wi-Fi и Ethernet добавить маршрутизацию данных по nRF24 или LoRa или по любой другой беспроводной сети. Таким образом можно сделать любой роутер для вашей IoT системы.

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

Итоги


Теперь давайте подведём итоги этого небольшого исследования: в общем, несмотря на некоторые косяки и детские болезни, контроллер LILYGO TTGO T-Internet-POE ESP32 LAN8270A мне понравился это отличный инструмент для построения IoT систем, особенно если вы обладаете соответствующей квалификацией и не лишены фантазии и креативного подхода к своему творчеству.

Плюсы и минусы LILYGO TTGO T-Internet-POE ESP32 LAN8270A.

Плюсы:
  • Это работает!
  • Законченное интегрированное решение Wi-Fi + Ethernet + POE + GPIO
  • Хорошая работа без зависаний и перезагрузок (проблем при тестировании выявлено не было)
  • Возможность одновременной работы по двум интерфейсам

Минусы:
  • Тотальное отсутствие документации, примеров и пояснений
  • Относительно высокий порог входа
  • Детские болезни и мелкие косяки в реализации
Подробнее..

ESP32 LVGL и круглый дисплей

25.03.2021 16:19:55 | Автор: admin

В прошлом году, после выхода видео про дисплей GC9A01 на канале "Электроника в объективе", я решил, что обязательно должен что-то на нем собрать, да еще и с использованием графической библиотеки LVGL. Заказал 2 таких дисплея, один на отладочной плате, второй отдельно только дисплей со шлейфом.

Так как я не очень люблю когда все соединено на проводах или макетных платах, (постоянно что-то отваливается), и хотелось какое-то законченное устройство в корпусе, решил поискать подходящий вариант и уже под него делать плату, ничего из стандартных корпусов для РЭА найти не удалось. Думал уже делать в виде съемной конструкции для своей отладочной платы на ESP32, но потом нашел подходящий вариант, (из под консилера :) ), он идеально подходил под мою задумку, был круглый открывающийся и имел прозрачное окно в размер дисплея. решено было делать плату под него.

Основные требования по устройству были: чип ESP32 так же для перспективы заложил драйвер RS-485 и слот для microSD карточки, так же хотелось иметь возможность питать все от usb и в перспективе иметь возможность подать внешнее питание. Так как разъем все равно ставить, хотелось сразу иметь возможность и программировать esp32 без дополнительных подключений. Но количество свободного места оказалось крайне мало, ставить CP2102 было дороговато, а CH340 с ее габаритами и кварцем не хотелось. Случайно увидел что есть компактная микросхема CH340N SOIC-8, преобразователь USB-UART с минимальной обвязкой в корпусе SOP-8. Ее и решено было использовать.

Так как на одной плате все не помещалось, разделил на две, верхняя ESP32 и дисплей, снизу питание, microSD слот, драйвер RS485 и USB-UART преобразователь. Для связи между нижней и верхней платой решил использовать гибкий плоский шлейф FFC и коннекторы FPC с шагом 0.5. Готовых шлейфов под размер и количество линий в наличии не было, решил делать из широкого и длинного шлейфа, думал что не получится разделить, но оказалось что это возможно. Для удешевления заказа объединил две платы в одну, с последующим удалением перемычки между ними, такая компоновка позволяет использовать верхнюю плату и под другие проекты с выводом информации на дисплей.

Общий вид печатной платы

Прикинув все размеры компонентов, решил уменьшить толщину платы до минимально возможного размера без увеличения стоимости, заказал с толщиной 0,6мм с зеленой маской. При такой толщине их можно резать чуть ли не ножницами, разделение на 2 платы заняло пару минут. Как оказалось не обошлось и без "косяков", перепутал выводы micro-USB разъема, разместил зеркально, пришлось резать дорожки и переделывать, после залил лаком. Так же вывод Reset дисплея соединил не с портом общего назначения, а с сигналом разрешения работы EN, в принципе на работе это не отразилось, но нет возможности сброса экрана из программы. Вся остальная схема запустилась без проблем, прошивается через micro-USB, через этот же разъем можно подключаться по Modbus например используя Modbus poll/mbslave, задействовал линии rx и tx которые идут на драйвер ADM3485 и они же используются при программировании.

Для создания проекта использовал ESP-IDF и порт LVGL. Данный пример содержит множество готовых драйверов дисплеев и примеры большинства виджетов библиотеки LVGL. Так как дисплей не имеет тачскрина, то мне были интересны примеры вывода графики в виде различных манометров и других, заточенных под круглые дисплеи, виджетов. Пример из библиотеки мне не очень понравился, хотелось использовать что-то более реалистичное, так как сам дисплей позволяет выводить довольно качественную картинку, решил использовать готовые подложки, их необходимо подготовить под разрешение 240x240, в принципе можно использовать любые подходящие, единственное перед конвертацией в графическом редакторе необходимо убрать стрелки, так как стрелка будет управляться отдельно из программы. Варианты стрелок так же можно подготовить в разном стиле и цвете, для разных подложек. Стрелка должна быть симметрична, так как из программы она будет вращаться относительно центра дисплея. Вся подготовленная под размер экрана графика конвертируется через сервис imageconverter в .с файл, который затем подключается в программе. Самый простой пример для манометра содержит два объекта, сама подложка она статическая и стрелка- изменяющийся объект.

img0 = lv_img_create(lv_scr_act(), NULL);//фон манометраlv_img_set_src(img0, &man5);lv_obj_align(img0, NULL, LV_ALIGN_CENTER, 0, 0);//стрелка, угол 60lv_img_set_angle(s6, 600);

Задание для стрелки выдается в отдельной задаче вызываемой с требуемым периодом:

static void update_time(void *arg){//    get_time(); //получить значение часов, минут, секундcur_time_s++;//    lv_img_set_angle(  lvHour, cur_time_h*30*10);//    lv_img_set_angle(  lvMinute, cur_time_m*6*10);    lv_img_set_angle(  lvSecond, cur_time_s*6*10);//задание угла для секундной стрелки}

Минимальный шаг задания 0,1, окружность разделена на 3600 частей. В зависимости от выбранной подложки можно рассчитать min и max задание в пределах шкалы. Остается преобразовать полученное значение от датчика в угол задания. Для демонстрации часов происходит управление тремя стрелками, достаточно поднять на ESP32 NTP сервер и получить текущее значение часов. минут, секунд (в моем примере NTP сервис не запущен) Для того, чтобы каждый раз не корректировать файлы стрелок под выбранный циферблат их можно масштабировать:

//удлинить минутную стрелкуlv_img_set_zoom(lvMinute, 340);

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

Из недостатков можно отметить "тормознутость" на больших диагоналях из-за невозможности выделить полноценный буфер под полное разрешение экрана, особенно это проявляется при обновлении всего экрана, когда происходит скроллинг или открытие другого таба. Для простых кнопочных интерфейсов эта проблема не так актуальна, так как обновляется только область кнопки. Для представленного дисплея с разрешением 240x240 проблема не так заметна, но при использовании нескольких подложек сильно увеличивается размер прошивки. Пока вижу вариант загрузки графики с SD карты или использования ESP32 с памятью psram и размещением в ней графики или буфера. На моих платах интерфейс spi дисплея и sd карты разведены на одних и тех же пинах и, насколько я знаю, пока не удалось заставить их работать в тандеме, есть отдельные упоминания о работе но я пока не проверял. Если у кого-то есть работающий пример напишите в комментариях. Так же я пока не пробовал использовать память psram для размещения буфера дисплея, без этого памяти остается не так много. В чипе который используется ее нет, но она есть в чипе ESP32-WROVER на второй плате к которой можно подключить дисплей от Adafruit 4,3" , Waveshare 7" и данный круглый через переходник со шлейфом.

Общий вид отладочной платы

Дальше планирую проверить подключение по Modbus и можно использовать как небольшую панель для чтения параметров из внешних устройств. Так же на самой плате возле выводов модуля сделаны полигоны для возможности подключения датчиков, например ds18b20 или влажности/давления, внутри их размещать смысла нет, так как при закрытом корпусе там довольно жарко, но можно вывести наружу через отверстие. Так же можно организовать mqtt и получать данные с удаленного датчика. Наконец, есть еще bluetooth который я даже не включал, но думаю и там есть множество применений. Далее видео получившегося устройства с демонстрацией разных вариантов экранов.

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

Схему не привожу, так как ее у меня нет, плата разводилась "на лету" за чашкой чая вечером, обвязку для микросхем можно взять в pdf, esp32 так же позволяет переназначать все пины используемые в проекте. Для запуска примеров достаточно взять ESP32-DevKitC и плату с экраном GC9A01. Пины используемые в моем проекте: MOSI-12 CLK-14 CS-23 DC-22. SPI лучше использовать на пинах по умолчанию, тогда его можно будет запускать на "максималках", у меня первая плата была разведена не так и эту я оставил для совместимости проектов.

Перечень основных элементов:

  • ESP-WROOM-32

  • ADM3485EARZ-REEL7

  • CH340N

  • AMS1117

  • FPC SC 0.5MM 14P CB

  • 12PIN SPI TFT LCD GC9A01

Мой демо проект на github в папке components/image конвертированные файлы подложек для экранов.

Тут более свежие версии библиотеки с примерами.

Подробнее..

.NET nanoFramework платформа для разработки приложений на C для микроконтроллеров

25.03.2021 22:22:00 | Автор: admin
nanoframework

.NET nanoFramework это бесплатная платформа с открытым исходным кодом, основанная на .NET и предназначена для малых встраиваемых устройств, микроконтроллеров. С ее помощью можно разрабатывать различные устройства для Интернета вещей, носимые устройства, научные приборы, робототехнические устройства, можно создавать прототипы и даже использовать на промышленном оборудование.

.NET nanoFramework является малой версией большого .NET Framework предназначенного для настольных систем. Разработка приложений ведется на языке C# в среде разработки Visual Studio. Сама платформа является исполнительной средой .NET кода, это позволяет абстрагироваться от аппаратного обеспечения и дает возможность переносить программный код с одного микроконтроллера на другой, который тоже поддерживает .NET nanoFramework. Программный код на C# для настольных систем, без изменений или с небольшой адаптацией (необходимо помнить про малый объем оперативной памяти) исполнится на микроконтроллере. Благодаря этому, разработчики на .NET с минимальными знаниями в области микроэлектроники смогут разрабатывать различные устройства на .NET nanoFramework.

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

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

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

Особенности .NET nanoFramework:

  • Может работать на 32- и 64-разрядных микроконтроллерах ARM, с наличием всего 256 КБ флэш-памяти и 64 КБ ОЗУ.
  • Работает нативно на чипе, в настоящее время поддерживаются устройства ARM Cortex-M и ESP32.
  • Поддерживает самые распространенные интерфейсы такие как :GPIO, UART, SPI, I2C, USB, networking.
  • Обеспечивает встроенную поддержку многопоточности.
  • Включает функции управления электропитанием для обеспечения энергоэффективности, например, устройств работающих от аккумуляторных батарей.
  • Поддерживает совмещение управляемого кода на C# и неуправляемого кода на C/C++ в одном проекте.
  • Автоматическая сборка мусора благодаря сборщику мусора.

В сравнение с другими платформами:

  • Доступен интерактивный отладчик при запуске кода на самом устройстве с точками останова.
  • Есть развитая и бесплатная среда программирования с Microsoft Visual Studio.
  • Поддержка большого количества недорогих плат от различных производителей, включая: платы Discovery и Nucleo от ST Microelectronics, Quail от Mikrobus, Netduino от Wilderness Labs, ESP32 DevKit C, Texas Instruments CC3220 Launchpad, CC1352 Launchpad и NXP MIMXRT1060-EVK.
  • Легко переносится на другие аппаратные платформы и устройства на ОС RTOS .В настоящее время совместимость обеспечивается в соответствие с CMSIS и ESP32 FreeRTOS.
  • Полностью бесплатное решение с открытым исходным кодом, никаких скрытых лицензионных отчислений. От основных компонентов до утилит, используемых для создания, развертывания, отладки и компонентов IDE.


Предыстория


Вначале было Слово и было это Слово .NET Micro Framework от компании Micrsoft. До появления .NET nanoFramework, в Microsoft любительский проект перерос в серьезную платформу .NET Micro Framework, которая быстро завоевала популярность на американском рынке. Такая компания GHI Electronics с 2008 года, построила весь свой бизнес на разработке микроконтроллеров и решений на базе .NET Micro Framework. В портфолио GHI Electronics были небольшие микроконтроллеры в стиле Arduino FEZ Domino и весьма производительные с несколькими мегабайтами ОЗУ (для микроконтроллеров это весьма круто).

Микроконтроллеры компании GHI Electronics

GHI Electronics Modules

Устройства могли работать практически с любой периферией, была поддержка стека TCP/IP, WiFI, обновления по воздуху. Была даже ограниченная реализация СУБД SQLite, поддержка USB Host и USB Client. Не трудно понять что компания быстро смогла себе сделать имя, стать основным разработчикам решений на .NET Micro Framework, продукцию которой постановляют во все страны мира.

В 2014 г. в проекте Школьный звонок на .NET Micro Framework с удаленным управлением мною использовалась плата FEZ Domino от GHI Electronics. Так же было и множество других проектов таких как Netduino.

FEZ Domino

FEZ Domino

В октябре 2015 года на GitHub был опубликован релиз .NET Micro Framework v4.4, этот релиз оказался последним. Компании Micrsoft отказалась дальше развивать платформу, с этого момента начинает свою историю проект nanoFramework (с 2017 года), в основе которого кодовая база .NET Micro Framework. Многие основные библиотеки были полностью переписаны, некоторые перенесены как есть, было проведено множество чисток и улучшений кода. Энтузиасты встраиваемых систем, гики увлеченные программированием, возродили платформу!

Работа над платформой ведется в основном в свободное время. Некоторые из основных участников работают в компаниях, которые активно спонсируют .NET nanoFramework и предлагают свои рабочие часы для продвижения проекта. Если вы используете .NET nanoFramework как часть своего продукта или просто хотите его поддержать, можете сделать пожертвование. Это позволяет оплачивать затраты на инфраструктуру и уделять больше времени на реализацию проекта.


Архитектура платформы


Платформа включает в себя уменьшенную версию .NET Common Language Runtime (CLR) и подмножество библиотек базовых классов .NET вместе с наиболее распространенными API-интерфейсами, включенными в универсальную платформу Windows (UWP). В текущей реализации, .NET nanoFramework работает поверх ChibiOS которая поддерживается, некоторыми платами ST Microelectronics, Espressif ESP32, Texas InstrumentsCC3220 Launchpad,CC1352 Launchpad и NXP MIMXRT1060-EVK. Разработка ведется в Microsoft Visual Studio или Visual Studio Code, отладка производится непосредственно на устройстве в интерактивном режиме.

Общая архитектура


Для того чтобы эффективно писать код на C # на микроконтроллере, необходимо нужно понимать (на высоком уровне), как это работает. Весь код в микроконтроллере делится на 4 логических компонента, как показано ниже:

nanoframework architecture

На самом высоком уровне находится ваше приложение на C #, которое необходимо запускать на MCU. Ниже располагается уровень CLR, исполнительная среда для нашей программы. Загрузчик базовый компонент любого микроконтроллера, запускает среду CLR при включении MCU. Наконец, на самом низком уровне у нас есть MCU.

Для использования .NET nanoFramework необходимо загрузить среду nanoCLR на микроконтроллер. После этого приложение на C#, скомпилированное в виде бинарного файла, можно загружать на микроконтроллер.

Схема архитектуры .NET nanoFramework


nanoframework architecture

nanoCLR базируется на слое аппаратной абстракции (HAL). HAL предоставляет абстракцию устройств и стандартизует методы и функции работы с устройствами. Это позволяет использовать наборы функций которые одинаковы доступны на уровне абстракции платформы (PAL) и конкретных драйверов. Среда nanoCLR построена на PAL и содержит некоторые ключевые библиотеки, такие как mscorlib (System и несколько других пространств имен), которые всегда используются. Модульность .NET nanoFramework позволяет добавлять пространства имен (namespaces) и классы, связанные с nanoCLR.


ChibiOS


ChibiOS/RT компактная многозадачная операционная система реального времени (ОСРВ). Предназначена для встраиваемых приложений, работающих в реальном времени. Эта ОСРВ отличается высокой мобильностью, компактными размерами и, главным образом имеет свою собственную уникальную архитектуру, которая как никак подходит для быстрого и эффективного переключения контекста.

Основные характеристики:

  • Эффективное и портативное ядро.
  • Лучшая в своем классе реализация переключения контекста.
  • Множество поддерживаемых платформ.
  • Статичная архитектура все статически выделяется во время компиляции.
  • Динамические расширения динамические объекты поддерживаются как дополнительный слой надстройки статичного ядра.
  • Богатый набор примитивов для ОСРВ: потоки (threads), виртуальные таймера (virtual timers), семафоры (semaphores), мьютексы (mutexes), переменные условия/синхронизации (condition variables), сообщения (messages), очереди (queues), флаги событий (event flags) и почтовый ящик (mailboxes).
  • Поддержка алгоритма наследования для мьютексов.
  • HAL-компонент поддержки различных абстрактных драйверов устройств: порт, последовательный порт, ADC, CAN, I2C, MAC, MMC, PWM, SPI, UART, USB, USB-CDC.
  • Поддержка внешних компонентов uIP, lwIP, FatFs.
  • Инструментарий для отладки ОСРВ

Поддерживаемые платформы:

  • ARM7, ARM9
  • Cortex-M0, -M0+, -M3, -M4, -M7
  • PPC e200zX
  • STM8
  • MSP430
  • AVR
  • x86
  • PIC32

Области применения ChibiOs/RT:

  • Автомобильная электроника.
  • Робототехника и промышленная автоматика.
  • Бытовая электроника.
  • Системы управления электроэнергией.
  • DIY.

ChibiOS/RT также был портирована на Raspberry Pi, и были реализованы следующие драйверы устройств: порт (GPIO), Seral, GPT (универсальный таймер), I2C, SPI и PWM.


Поддерживаемые устройства


Поддерживаемые устройства делятся на две категории: основные платы и поддерживаемые сообществом.

Основные платы:

  • ESP32 WROOM-32, ESP32 WROOM-32D, ESP32 WROOM-32U, ESP32 SOLO-1
  • ESP-WROVER-KIT, ESP32 WROVER-B, ESP32 WROVER-IB
  • STM32NUCLEO-F091RC
  • STM32F429IDISCOVERY
  • STM32F769IDISCOVERY
  • OrgPal PalThree
  • CC1352R1_LAUNCHXL
  • CC3220SF_LAUNCHXL
  • MIMXRT1060 Evalboard
  • Netduino N3 WiFi

Платы поддерживаемые сообществом:

  • ESP32 ULX3S
  • STM32NUCLEO144-F746ZG
  • STM32F4DISCOVERY
  • TI CC1352P1 LAUNCHXL
  • GHI FEZ cerb40 nf
  • I2M Electron nf
  • I2M Oxygen
  • ST Nucleo 144 f439zi
  • ST Nucleo 64 f401re/f411re nf
  • STM NUCLEO144 F439ZI board
  • QUAIL


Пример программы


Примеры кода представлены в разделе nanoframework/Samples. Рассмотрим базовый пример, Blinky пример программы позволяющей мигать встроенным светодиодом на плате ESP32-WROOM:

using System.Device.Gpio;using System;using System.Threading;namespace Blinky{public class Program    {        private static GpioController s_GpioController;        public static void Main()        {            s_GpioController = new GpioController();            // ESP32 DevKit: 4 is a valid GPIO pin in, some boards like Xiuxin ESP32 may require GPIO Pin 2 instead.            GpioPin led = s_GpioController.OpenPin(4,PinMode.Output);            led.Write(PinValue.Low);            while (true)            {                led.Toggle();                Thread.Sleep(125);                led.Toggle();                Thread.Sleep(125);                led.Toggle();                Thread.Sleep(125);                led.Toggle();                Thread.Sleep(525);            }        }            }}

Программный код простой и понятный. Не требуется никаких детальных знаний аппаратной платформы, все что нужно знать, это распиновку платы. Вначале кода объявляется глобальный объект GpioController, затем устанавливается контакт с номером 4 на вывод, после этого в коде идет простое инверсное переключение состояния. Это все что требуется знать.


Что сейчас доступно из коробки?


Аппаратные интерфейсы:

  • Windows.Devices.WiFi работа с Wi-Fi сетью.
  • nanoFramework.Devices.Can работа с CAN шиной. CAN (Controller Area Network) стандарт промышленной сети, ориентированный, прежде всего, на объединение в единую сеть различных исполнительных устройств и датчиков, заменяет устаревшую шину RS 485. Используется в АСУ ТП, на заводах и фабриках.
  • 1-Wire 1-Wire интерфейс, используется для подключения одного/нескольких температурных датчиков DS18B20.
  • Windows.Devices.I2c, System.Device.I2c I2C шина для подключения нескольких устройств.
  • Windows.Devices.Spi SPI шина.
  • Windows.Devices.Adc аналого-цифровой преобразователь (АЦП).
  • Windows.Devices.Pwm широтно-импульсная модуляция (ШИМ).
  • System.Devices.Dac - цифро-аналоговый преобразователь (ЦАП).

Классы:

  • Windows.Devices.SerialCommunication работа с последовательным интерфейсом.
  • MQTT MQTT клиент, порт популярной библиотеки M2Mqtt. Библиотека предназначена для отправки коротких сообщений, используется для Интернета вещей и M2M взаимодействия.
  • System.Net.Http.Server и System.Net.Http.Client готовые классы Web-сервера и Web-клиента с поддержкой TLS/SSL.
  • Json работа с данными в формате Json.
  • nanoFramework.Graphics библиотека работы с отображения графических примитивов на LCD-TFT дисплеях.
  • System.Collections коллекции объектов.
  • Discord bot реализация Discord бота.
  • Json Serializer and Deserializer Json сериализацияя/десериализация.

Сетевые протоколы:

  • AMQP.Net Lite Облегченная версия открытого протокола AMQP 1.0 для передачи сообщений между компонентами системы. Основная идея заключается в том, что отдельные подсистемы (или независимые приложения) могут обмениваться произвольным образом сообщениями через AMQP-брокера, который осуществляет маршрутизацию, гарантирует доставку, распределяет потоки данных, предоставляет подписку на нужные типы сообщений. Используется в инфраструктуре Azure, поддерживает шифрование TLS.
  • SNTP протокол синхронизации времени по компьютерной сети.


Библиотеки классов


В таблице представлена общая организация библиотек классов .NET nanoFramework. Приведенные ниже примеры относятся к ChibiOS (которая в настоящее время является эталонной реализацией .NET nanoFramework):

Библиотека класса Название Nuget пакета
Base Class Library (also know as mscorlib) nanoFramework.CoreLibrary
nanoFramework.Hardware.Esp32 nanoFramework.Hardware.Esp32
nanoFramework.Runtime.Events nanoFramework.Runtime.Events
nanoFramework.Runtime.Native nanoFramework.Runtime.Native
nanoFramework.Runtime.Sntp nanoFramework.Runtime.Sntp
Windows.Devices.Adc nanoFramework.Windows.Devices.Adc
Windows.Devices.I2c nanoFramework.Windows.Devices.I2c
Windows.Device.Gpio nanoFramework.Windows.Devices.Gpio
Windows.Devices.Pwm nanoFramework.Windows.Devices.Pwm
Windows.Devices.SerialCommunication nanoFramework.Windows.Devices.SerialCommunication
Windows.Devices.Spi nanoFramework.Windows.Devices.Spi
Windows.Devices.WiFi nanoFramework.Windows.Devices.WiFi
Windows.Networking.Sockets nanoFramework.Windows.Networking.Sockets
Windows.Storage nanoFramework.Windows.Storage
Windows.Storage.Streams nanoFramework.Windows.Storage.Streams
System.Net nanoFramework.Windows.System.Net

Все дополнительные пакеты добавляются с помощью системы Nuget, как это принято в .NET Core.


Unit-тестирование


nanoframework unit test architecture

Тестирование настольного приложения на рабочей станции не вызывает никаких проблем, но все обстоит иначе, если необходимо тестировать приложение для микроконтроллера. Исполнительная среда должна быть эквивалентна по характеристикам микроконтроллеру. В Unit тестирование кода на C#, используется концепция Адаптера (Adapter). Для тестовой платформы Visual Studio (vstest) был разработан специальный компонент nanoFramework.TestAdapter. В нем реализовано, два интерфейса для детального описания конфигурации и третий для описания специфических параметров, таких как time out, в зависимости от целевой среды исполнения, на реальном оборудование или в Win32 nanoCLR. Механизм проведения тестов на Win32 nanoCLR и реальном оборудовании, одинаков. Единственное отличие это консоль вывода, которая в случае реального оборудования отправляет данные в порт отладки.

Один из интерфейсов называется ITestDiscoverer, который используется Visual Studio для сбора возможных тестов. vstest вызовет адаптер для любой запущенной сборки и передаст бинарный файл dll или exe, соответствующую определенному условию сборки (пока у нас нет TFM, и мы используем небольшой хак и основной .NET Framework 4.0 ). Затем nanoFramework TestAdapter анализирует каталоги, чтобы найти nfproj, анализируя файлы cs, глядя на определенные атрибуты Test, определенные для nanoFramework. На основе этого составляется список, который передается обратно.

Этот хак выполняется с помощью файла с расширением .runsettings с необходимым минимумов элементов ( для запуска приложения в Win32 nanoCLR, параметр IsRealHardware необходимо выставить в false, в случае реального устройства true):

nanoframework unit test

Когда выполняется сборка проекта отрабатывает триггер интерфейс ITestExecutor. В случае, если вы работаете в контексте Visual Studio, передается список тестов (индивидуальный или полный список), и именно здесь запускается nanoFramework.nanoCLR.Win32.exe как процесс, передающий nanoFramework.UnitTestLauncher.pe, mscorlib.pe, nanoFramework.TestFramework.pe и, конечно же, тестовую библиотеку для исполняемого файла.

nanoFramework Unit Test загрузит тестовую сборку и выполнит тесты с начиная с первого Setup, затем TestMethod и, наконец, тесты Cleanup.

Выходные данные этого процесса перенаправляются в TestAdapter, которые затем разбираются. Важное примечание: UnitTestLauncher необходимо собрать и развернуть при отладке! В противном случае он никогда не сможет ничего вывести в консоль. Чтобы запустить тест на реальном оборудование, нужно просто добавить строку ввода в файл runsettings, как указано выше.

По окончании выполнения тестов, возвращается статусы. Простой строковый вывод со статусом теста, имени метода и времени его выполнения и/или исключение. Тест пройден: MethodName, 1234 или Test failed: MethodName, подробное исключение. Это передается обратно в vstest, а затем отображается в Visual Studio.

Для Unit-тестирования необходимо в проект добавить NuGet пакет nanoFramework.TestFramework. Или использовать готовый проект для Unit-тестирования в Visual Studio, это самый простой способ! Он автоматически добавит в проект NuGet и .runsettings.


Лицензирование


Весь исходный код .NET nanoFramework распространяется по лицензией MIT, включая nf-interpreter, классы библиотек, расширение Visual Studio и все сопутствующие утилиты.

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

Для коммерческого использования ChibiOS, необходимо приобретать лицензию. Так же можно лицензировать отдельные компоненты.

Но если количество выпущенных коммерческих устройств с ChibiOS не превышает 500 ядер, то оплачивать лицензию не требуется. Свыше этого объема приобретаются пакеты лицензий на 500, 1000, 5000 ядер или неограниченно. При бесплатном коммерческом использование некоторые функции недоступны и в своем продукте необходимо реализовать рекламу ChibiOS.

Управляемые приложения managed apps (C#) запущенные на .NET nanoFramework не компилируются и не собираются ChibiOS, а интерпретируются на лету. Поэтому рассматриваются как отдельный компонент от встроенного ПО. Таким образом, схема лицензирования ChibiOS не распространяется на приложения C#, и не зависит от условий лицензирования ChibiOS.


Использование в промышленном сфере


Американская компания OrgPal.Iot специализируется на аппаратных и программных решениях для Интернета вещей (IOT), которые осуществляют сбор данных телеметрии для отдаленнейшего анализа, управление инфраструктурой через частное облако. Компания предоставляет конечные устройства и шлюзы для передачи данных на рабочие станции, сервера и мобильные устройства. Решения совместимы с Azure IoT.

Основные направление компании это мониторинг инфраструктуры:

  • Промышленного производства
  • Нефтяных месторождений
  • Панелей солнечных электростанций
  • Систем управления электропитанием

Одно из разработанных устройств компании это PalThree. PalThree сертифицированное устройство Azure IoT Ready Gateway & Edge Point для сбора данных и телеметрии с последующей передачей в облако Azure IoT. На борту большой набор входных интерфейсов для получения данных о технологических процессах. Устройство основано на STM32 ARM 4 и ARM 7, поставляется в двух вариантах с микроконтроллерами STM32F469x и STM32F769x, с 1 МБ SDRAM на плате, флэш-памятью SPI и QSPI. Программное обеспечение основано на .NET nanoFramework, с ChibiOS для STM32.

Как это работает

nanoframework palthree sensors cloud

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

Цистерны для хранения нефтегазовых продуктов
nanoframework oil tank palthree

Шкаф с PalThree
nanoframework palthree close up


Web-сервер с поддержкой: REST api, многопоточности, параметров в URL запросе, статических файлов.


Специально для .NET nanoFramework, Laurent Ellerbach разработал библиотеку nanoFramework.WebServer, которая по своей сути является упрощенной версией ASP.NET.

Возможности Web-сервера:

  • Обработка многопоточных запросов
  • Хранение статических файлов на любом носителе
  • Обработка параметров в URL запросе
  • Возможность одновременной работы нескольких веб-серверов
  • Поддержка команд GET/PUT и любых другие
  • Поддержка любого типа заголовка http-запроса
  • Поддерживает контент в POST запросе
  • Доступно использование контроллеров и маршрутизации
  • Хелперы для возврата кода ошибок, для REST API
  • Поддержка HTTPS
  • Декодирование/кодирование URL

Ограничение: не поддерживает компрессию ZIP в запросе и ответе.

Для использования Web-сервера необходимо просто указать порт и добавить обработчик запросов:

using (WebServer server = new WebServer(80, HttpProtocol.Http){    // Add a handler for commands that are received by the server.    server.CommandReceived += ServerCommandReceived;    // Start the server.    server.Start();    Thread.Sleep(Timeout.Infinite);}

Так же, можно передать контроллер, например ControllerTest, и будет использоваться декоратор для маршрутизации и методов:

using (WebServer server = new WebServer(80, HttpProtocol.Http, new Type[] { typeof(ControllerPerson), typeof(ControllerTest) })){    // Start the server.    server.Start();    Thread.Sleep(Timeout.Infinite);}

В следующем примере, определяется маршрут test и т.д., в котором определяется метод GET, и test/any:

public class ControllerTest{    [Route("test"), Route("Test2"), Route("tEst42"), Route("TEST")]    [CaseSensitive]    [Method("GET")]    public void RoutePostTest(WebServerEventArgs e)    {        string route = $"The route asked is {e.Context.Request.RawUrl.TrimStart('/').Split('/')[0]}";        e.Context.Response.ContentType = "text/plain";        WebServer.OutPutStream(e.Context.Response, route);    }    [Route("test/any")]    public void RouteAnyTest(WebServerEventArgs e)    {        WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);    }}

Функция RoutePostTest будет вызываться каждый раз, когда вызываемый URL-адрес будет test, Test2, tEst42 или TEST, URL-адрес может быть с параметрами и методом GET. RouteAnyTest вызывается всякий раз, когда URL-адрес является test/any, независимо от метода. По умолчанию маршруты не чувствительны к регистру, и атрибут должен быть в нижнем регистре.

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

Cooming Soon


В продолжение к публикации, будет практическая работа с загрузкой CLR на ESP32 и Nucleo, с написанием первой программы на C#.

Страница проекта .NET nanoFramework
Подробнее..

Зачем все ставят вентиляторы в туалет или как мы решили сделать умный вентилятор, история по DIY

14.04.2021 10:21:24 | Автор: admin

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

Как должна работать вентиляция в 99% наших квартир? И в 1% оставшихся.

Система вентиляции в подавляющем большинстве случаев организуется, как естественный приток и естественная вытяжка, в соответствии с СП 60.13330.2016 ОТОПЛЕНИЕ, ВЕНТИЛЯЦИЯ И КОНДИЦИОНИРОВАНИЕ, это означает, что приточный воздух должен попадать в комнаты через ограждающие конструкции (неплотности окон, режим микропроветривания окон или приточные клапаны) в помещение и вытеснять загрязненный воздух в грязные зоны: кухни, санузлы, ванные, в общем туда, где есть вытяжные каналы. Схема ниже.

Другой альтернативой вентиляции является применение бризеров, вентиляционных установок, рекуператоров про это написано в нашей первой статье на Хабр О современной вентиляции . Это хорошее решение если устраивает по цене.

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

Три фактора для правильной работы естественной вентиляции:

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

  2. Беспрепятственное прохождение воздуха от окна до вытяжного канала: организуется либо за счет подпила полотна на 15-20 мм или установкой переточной решетки в дверях.

  3. Удаление загрязненного воздуха через вытяжные каналы круглый год.

Так работают системы вентиляции не только у нас, но и у европейцев. Например, приток воздуха через приточные клапаны и удаление через вытяжные каналы организуется в Германии, Франции, Англии, Польше, Финляндии и странах Скандинавии (что подкреплено законодательно).

Три фактора для правильной работы естественной вентиляции:

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

  2. Беспрепятственное прохождение воздуха от окна до вытяжного канала: организуется либо за счет подпила полотна на 15-20 мм или установкой переточной решетки в дверях.

  3. Удаление загрязненного воздуха через вытяжные каналы круглый год.

Так работают системы вентиляции не только у нас, но и у европейцев. Например, приток воздуха через приточные клапаны и удаление через вытяжные каналы организуется в Германии, Франции, Англии, Польше, Финляндии и странах Скандинавии (что подкреплено законодательно).

Работа естественных систем вентиляции зависит от тяги. Что это такое и зачем.

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

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

Это похоже на движение воздушного шара, который нагревают горелкой.

Если горелки нет, то шар не летит если на улице тепло, то вентиляция, увы, не работает.

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

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

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

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

Нужно иметь возможность управлять своей системой вентиляции. Именно поэтому вытяжные вентиляторы решают проблему наличия вентиляционной тяги и позволяют проветривать всю квартиру.

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

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

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

Обычно люди не знают, что этот вентилятор может улучшить климат во всей квартире, если его не выключать. Реальная производительность бытовых вентиляторов подключенных в сеть воздуховодов составляет от 30 до 50 м3/ч. Что ниже норм по вентиляции всей квартиры минимум 110 м3/ч. Поэтому и должен он трудится всегда, когда мы хотим, чтобы работала вентиляция.

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

Вот обзор работы вентиляции в гостинице Германии по такой схеме известным в своих кругах блогером: https://www.youtube.com/watch?v=IEwSiI6gZ4M

Таким образом, для правильной работы системы вентиляции нужно:

  • круглый год применять вытяжные вентиляторы для создания напора,

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

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

Обычный или умный вентилятор, в чем разница.

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

Пример обычных вентиляторов (не забываем гугл или яндекс):

  1. Пример Россия

  2. Пример Европа

Автоматических вентиляторов пока что мало на рынке, пример:

  1. Бытовой вентилятор РФ

  2. Вентилятор Европа

  3. Вентилятор для коттеджа

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

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

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

Такое решение дает ряд преимуществ:

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

  2. Шум от вентилятора локализован в санузле или ванной, наличие двери всегда экранирует шум. Такое решение работает тише, чем бризер (при таком решении вентилятор располагается в комнате).

  3. Энергоэффективно, так как на нагрев уличного воздуха в современных домах приходится до 40-50% затрат на отопление, рационально снижать объем вентиляции, когда она не востребована.

Как пришло в голову делать вентилятор для туалета умным.

Озадачившись работой вентиляции в своей новостройке, а точнее ее отсутствием, я начал разбираться как она должна работать.

Все оказалось просто, как написано выше, подавай свежий и удаляй загрязненный.

Подавать воздух я решил через приточный клапан, так как мне не понравилось бегать открывать и закрывать окно. А главное, как понять, что ты достаточно приоткрыл окно, и теперь воздух свежий? Если много открыл, то жалко тепла и холодно, если мало открыл, думаешь чем же дышит твой ребенок, ведь везде так много загрязняющих веществ таких как: высокий уровень СО2, фенолы из мебели, радон от стен и подвала и много еще чего непонятного.

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

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

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

Человек я не жадный, понимаю, греть улицу будет хорошо, так как будет быстрее приходить лето. Но жалко ведь повышать таким образом уровень СО2 на планете.

Поэтому задумался, как вентилятору понять, что дома есть люди?

Оказалось, что это достаточно просто организовать по датчику качества воздуха.

Вот, к стати, интересный анекдот иллюстрирующий как это работает от известного блогера строительной сферы видео анекдот ( 48 секунд).

История вентилятора, как делали и из чего состоит.

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

Идея понравилась, но как сделать мелкую серию?

Из анализа работы первого прототипа было решено применить следующие датчики для определения режимов работы:

  • датчик света,

  • датчик температуры/влажности SHT30 или HTU21,

  • датчик качества воздуха типа ccs-811 или sgp40,

Датчик качества воздуха решили ставить MOX, так как более продвинутые NDIR имеют больший габарит и цену. Выбранный датчик MOX измеряет VOC и эквивалент концентрации СО2, мы решили, что это лучший вариант. Так как в грязных зонах много органики и также нужно понимать уровень СО2.

Хотя мы понимаем, что лучшим решением будет датчик NDIR и VOC, но пока это дорого и, наверное, рано.

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

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

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

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

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

Этот прототип пробовали делать на ESP32 и на BLE nrf52 от Nordic, блок питания пробовали использовать заводской импульсный и самодельный без гальванической развязки конденсаторный как в розетке Редмонд, см. статью на Хабре.

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

У кого есть опыт с ESP32 и серверной частью, просьба, напишите в комментах, сложно ли это все делать?

Эксперименты с конденсаторными блоками привели к взрыву и были прекращены). Теперь все только с гальванической развязкой!

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

Решили сделать все тоже самое в более бюджетном корпусе, пример прототипа 2 ниже.

Прототип 2 работал хорошо и стоимость донора устраивала. Было проведено апробирование работы у тестеров.

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

Прототип 2 имеет следующие фичи:

  • режим работы по датчикам как по отдельности, так и совместно: качество воздуха (VOC+eCO2), температура влажность, освещенность,

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

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

Короткий обзор, прототипа 2.

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

Сейчас вентилятор состоит из 4х частей: базы с вентилятором, электронной платы управления с блютузом для подключения к смартфону (пока Андройд), крышки вентилятора, которую делаем индивидуально и лицевой панели. Лицевая панель крепится на магниты. Все датчики располагаются на плате с левой стороны, можно посмотреть на фото ниже.

Тестовую партию поставили через знакомых и старых клиентов, и сейчас тестируем рынок.

Узким местом прототипа 3 стал корпус, который приходится изготавливать по технологии мелкосерийного литья пластика. Пока решили изготавливать корпуса самостоятельно методом литья в силикон.

При этом пришлось набить много шишек и 60% крышек ушло в брак. Но теперь мы начали, что-то понимать в самой технологии литья в силикон, надеюсь, что это добавит к карме плюсиков.

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

Следующие крышки хотим делать литьем на ТПА на алюминиевых прессформах, так как настоящие стальные это минимум 15 тыс. зеленых. На рынке не много фирм, которые работают с алюминиевыми прессформами и реальных отзывов не нашел. Если кто-то сталкивался с алюминиевыми пресс формами пишите в комментах, обсудим подробнее. В основном сталь и делают в Китае.

Итого на выходе мы имеем:

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

  2. Удобный интерфейс для настройки режимов работы. Пока, что только для Андройда.

  3. Красивый дизайн с панелями разного цвета (по запросу, можем и индивидуальную изготовить).

  4. Тихою работу за счет качественной и хорошей крыльчатки.

Видеообзор с реальным устройством выглядит вот так

В текущей версии вентилятор имеет:

Ручные режимы работы для настройки работы по отдельным датчикам:

1. По скорости выбираем нужную скорость

2. По влажности работает по уставке влажности

3. По освещенности работает по включению света

4. По качеству воздуха выбираем в качестве уставки качество воздуха

Отдельно имеется режим Смарт (мы его так назвали).

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

Вот пример-скрин программы с режимами работы.

Что хотим добавить в вентилятор.

Многие пользователи на тестах попросили сделать удаленное управление через интернет. Как вы считаете нужно или нет?

Я считаю, что этот вентилятор должен настраиваться один раз в жизни и вы должны про него забыть, он должен работать Сам! Но все любят иметь возможность контролировать и, поэтому хотят удаленно управление. Так как разработка устройства с возможностью подключения к интернету сложнее по причине серверной части и полноценного приложения, то в планах делать систему на WI-FI второй очередью.

Команда

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

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

Кто знает, как искать единомышленников пишите. Мы ищем!

В итоге: для такого проекта понадобились скилы: схемотехника, программиста, разработчика ПО для смартфонов, конструктора и руководителя.

У нас запланирована серия устройств: автоматический вентилятор, приточное устройство, станция управления климатом, управление термостатами отопления от станции контроля климата.

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

Планы на будущее

Говорят, не нужно афишировать, а мы будем. Написав это здесь, мы будем стремится не опускать руки и двигаться по пунктам, не сбиваясь с ориентиров.

Итак:

  1. Сделать вентилятор не только D100 мм, но и D120 мм.

  2. Сделать вентилятор с WI-FI.

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

После выполнения этих пунктов хотим запустить для умную станцию управления по датчику СО2. Эта станция должна управлять как вентиляцией, так и отоплением.

Следующая статья планируется про приточное устройство, которое представлено выше, сейчас идут его тесты и доработка. Так как суровая зима 2020/21 показала некоторые промахи в конструкции.

Кому интересна тема управления климатом пишите и присоединяйтесь к нам в группе в телеграмме GreenVent, мы рады и критике, и новым идеям.

Главное, что Вы не равнодушны!

Подробнее..

Сетевой интерфейс для программируемого реле с поддержкой Telegram Bot и HomeKit

07.05.2021 14:19:42 | Автор: admin

Как я реализовал удаленное управление и мониторинг, для программируемого реле ПР200, используя разные сервисы (Telegram Bot, HomeKit) и протоколы (Modbus RTU, Modbus TCP, mqtt) и ESP32.

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

Долгое время я использовал сетевой интерфейс на основе модуля esp8266, на сегодня более перспективным вижу использование модуля esp32, долгое время я не рассматривал его из-за размеров, но впоследствии, проработав разные варианты, удалось не только вместить все на одной плате, но и сделать решение более универсальным и удобным.

Первая версия платы на основе ESP32

Во второй версии добавлен usb порт, что позволяет загрузить прошивку без использования дополнительных проводов и преобразователей. Базовая реализация изначально задумывалась с возможностью обновления прошивки по воздуху через ОТА, а для экспериментов проще использовать microUSB.

В обновленном варианте добавил ещё и кнопки сброса и загрузки при прошивке, а так же добавил поддержку модулей ESP32-WROVER с PSRAM, это позволит использовать больше памяти и расширит возможности.

В общем, структура взаимодействия сетевой платы с программируемым реле основана на протоколе modbus rtu, а с внешним миром варианты могут быть самые разнообразные от bluetooth до TelegramBot.

TelegramBot

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

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

Для универсальности взаимодействие бота с алгоритмом в приборе, использован режим чтения/записи сетевых регистров Modbus а разных форматах представления:

/R- целое 16 битное значение

/I-целое число занимающее 2 регистра

/F- число в формате float тоже 2 регистра.

После символа адрес в диапазоне 512-576, эти регистры можно читать и записывать, формат для записи /Xzzz=nnnn, для чтения достаточно отправить номер регистра в требуемом формате.

Для представления состояний регистра в битовых полях, можно отправить адрес в формате /Bzzz, ответ будет в виде 16 значения в булевом формате.

Apple HomeKit

Следующим этапом был сервис Apple HomeKit и приложение дом, как раз для управления освещением и другими точечными нагрузками он подходит лучше всего, я начал с 16 каналов, по количеству бит в регистре модбас.

После выхода обзора по такому применению,

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

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

Для быстрого добавления платы в приложение Дом, на web страницу добавлен QR код, второй вариант ввести код настройки, индивидуальный для каждой платы. Постарался упростить и минимизировать все настройки для быстрого старта.

Так же протестировал mqtt, идея задания топиков взята из версии платы для esp8266. Проверил поддержку датчиков 1-wire ds18b20, для их подключения к плате предусмотрены посадочные места под разъем, и сигнальные линии с резисторами, такой-же использовался в плате prsd на esp8266.

4 пина, два из которых +3.3v и gnd, позволяют задействовать 2 порта в качестве интерфейса 1-wire или i2c. I2C позволяет подключать всякую экзотику, которую практически невозможно состыковать в базовой поставке прибора. Например, датчик влажности/давления с I2C или RFID ридер.

Для быстрого просмотра значений регистров используется протокол Modbus TCP, запустив Modbus Poll на ПК или Virtuino/Kascada и другие приложения на Android, можно быстро организовать доступ и управление устройством с помощью телефона или планшета.

Остальные настройки WEB интерфейса представлены ниже:

WEB настройки

Для смены прошивки платы, когда она уже установлена в устройство, предусмотрен режим обновления по воздуху (OTA), для этого достаточно выбрать bin файл, после загрузки прошивки устройство перезагрузится и запустится обновленная версия. Так же можно перезагрузить плату в ручном режиме через web кнопку.

При первом старте, когда устройство не имеет настроек точки доступа и пароля и не может подключиться к сети wi-fi, плата включает режим точки доступа для подключения и ввода ssid и pass, после сохранения значений и перезагрузки если подключение к сети успешно, точка доступа выключается. Если токен Telegram bot введен, то после подключения и выхода в интернет, узнать IP адрес платы можно введя команду. Через бот можно получить и другую информацию.

Основные моменты по работе представлены в видео.

На данный момент прошивка находится в режиме доработки, демонстрационные версии будут доступны позже.

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

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

В меню бота есть возможность смотреть время бесперебойной работы в секундах, текущий и минимальный объем доступной Heap памяти, используя один и тот же токен для TelegramBot, можно читать параметры сразу с нескольких устройств, аналогично и на запись параметров. Т.е. любая команда, отправленная с телефона боту, отправляется всем устройствам с токеном, это происходит не всегда, иногда отвечает только часть устройств, и приходится делать запросы повторно. Сейчас все они не имеют привязки к имени, думаю сделать уникальные имена для отличия при запросах/ответах.

Используя несложный сетевой интерфейс с чипом ESP32, можно значительно расширить функционал программируемого реле ПР200 и в перспективе ПР103, куда можно установить сетевой интерфейс, другие модели ПР100/ПР102 потребуют внешний драйвер RS-485 для подключения снаружи, так как сетевые интерфейсы в них не съемные.

Наличие программатора на борту платы, позволяет создать собственные алгоритмы с быстрой загрузкой в устройство, необходимо лишь обеспечить обмен по протоколу Modbus на стандартных выводах UART esp32.

Подробнее..

Использование бюджетных JTAG-отладчиков в PlatformIO

09.05.2021 16:23:04 | Автор: admin

В этом туториале я хотел бы рассказать о том, как использовать ультрабюджетные JTAG-отладчики CJMCU FT232H и RV-Debugger-Lite в PlatformIO для прошивки и отладки устройств на платформах ESP32 и GD32. Полноценной инструкции на просторах интернета я не нашел, и в процессе настройки столкнулся со многими проблемами, поэтому этот туториал появляется здесь для вашего удобства. Оговорюсь сразу, что настройка прописана для Linux, но для Windows принципиальной разницы нет за исключением танцев с Zadig.

CJMCU FT232H + ESR32 Rev1 aka ESP32Dev

Имеется в виду вот этот дебаггер на основе чипа FT232H:

И вот эта плата на основе ESP-WROOM-32:

Настройки platformio.ini:

[env:esp32dev]platform = espressif32framework = arduinoboard = esp32devmonitor_speed = 115200upload_speed = 921600debug_tool = minimoduleupload_protocol = minimodule

Здесь у нас CJMCU FT232H за 700 рублей будет прикидываться отладчиком FT2232H MINI MODULE за 4500. Для этого необходимо внести следующие изменения в файл (закомментированные мной строки начинаются с ##):

/home/<username>/.platformio/packages/tool-openocd-esp32/share/openocd/scripts/interface/ftdi/minimodule.cfg

## FTDI MiniModule## http://www.ftdichip.com/Support/Documents/DataSheets/Modules/DS_FT2232H_Mini_Module.pdf#interface ftdi##ftdi_device_desc "FT2232H MiniModule"##ftdi_vid_pid 0x0403 0x6010ftdi_vid_pid 0x0403 0x6014# Every pin set as high impedance except TCK, TDI, TDO and TMSftdi_layout_init 0x0008 0x000b# nSRST defined on pin CN2-13 of the MiniModule (pin ADBUS5 [AD5] on the FT2232H chip)# This choice is arbitrary. Use other GPIO pin if desired.##ftdi_layout_signal nSRST -data 0x0020 -oe 0x0020## added# interface 1 is the uartftdi_channel 0reset_config none

Немаловажно закомментировать описание отладчика minimodule"ftdi_device_desc", иначеCJMCU FT232H не получит прав доступа. В том же файле мы меняем пару VID/PID, и ее же прописываем в:

/etc/udev/rules.d/99-platformio-udev.rules

SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014",GROUP="plugdev", MODE="0666"

чтобы отладчик определялся.

Подключение следующее:

Функция JTAG

CJMCU FT232H

ESP32Dev

TDO

AD2

GPIO15

TDI

AD1

GPIO12

TCK

AD0

GPIO13

TMS

AD3

GPIO14

+3.3V

3V3

GND

GND

CJMCU FT232H + Sipeed Longan Nano

Имеется в виду вот такая вот плата на основе чипа GD32VF103CBT6:

Настройки platformio.ini:

[env:sipeed-longan-nano]platform = gd32vframework = gd32vf103-sdkboard = sipeed-longan-nanomonitor_speed = 115200debug_tool = um232hupload_protocol = um232h

Здесь у нас CJMCU FT232H будет прикидываться отладчиком UM232H за 2000 рублей. Для этого необходимо внести следующие изменения в файл:

/home/<username>/.platformio/packages/tool-openocd-gd32v/share/openocd/scripts/interface/ftdi/um232h.cfg

## FTDI UM232H as a JTAG interface## http://www.ftdichip.com/Products/Modules/DevelopmentModules.htm#UM232H## This should also work with a UM232H-B, but that has not been tested.# Note that UM232H and UM232H-B are 3.3V only.#interface ftdi#ftdi_device_desc "UM232H"ftdi_vid_pid 0x0403 0x6014##ftdi_layout_init 0xfff8 0xfffb##ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100##ftdi_layout_signal nSRST -data 0x0200 -oe 0x0200# interface 1 is the uartftdi_channel 0# just TCK TDI TDO TMS, no resetftdi_layout_init 0x0008 0x000b#ftdi_layout_init 0x0c08 0x0f1breset_config none

Здесь у нас чип совпадает, поэтому только закомментируем ftdi_device_desc, и не забываем внести описанные ранее изменения в 99-platformio-udev.rules.

Возможно вам придется поставить недостающую библиотеку libhidapi-hidraw0:

sudo apt-get install -y libhidapi-hidraw0

Подключение следующее:

Функция JTAG

CJMCU FT232H

Sipeed Longan Nano

TDO

AD2

JTDO

TDI

AD1

JTDI

TCK

AD0

JTCK

TMS

AD3

JTMS

+3.3V

3V3

GND

GND

RV-Debugger-Lite + Sipeed Longan Nano

И напоследок я хочу описать, как использовать ультрабюджетный отладчик RV-Debugger-Lite за 200 рублей.

Настройки platformio.ini:

[env:sipeed-longan-nano]platform = gd32vframework = gd32vf103-sdkboard = sipeed-longan-nanomonitor_speed = 115200debug_tool = sipeed-rv-debuggerupload_protocol = sipeed-rv-debugger

Здесь мы будем прикидываться его старшим братом Sipeed RV-Debugger. Сейчас его в продаже нет, но, если мне не изменяет память, он стоил в пределах 1000 рублей. Для этого мы редактируем файл:

/home/<username>/.platformio/packages/tool-openocd-gd32v/share/openocd/scripts/interface/ftdi/sipeed-rv-debugger.cfg

interface ftdi##ftdi_device_desc "Dual RS232"ftdi_vid_pid 0x0403 0x6010#autoexit true#interface cmsis-daptransport select jtagftdi_layout_init 0x0008 0x001bftdi_layout_signal nSRST -oe 0x0020 -data 0x0020

Здесь мы только комментируем ftdi_device_desc, так как чип почти совпадает: у RV-Debugger FT2232D, а у RV-Debugger-Lite определяется как FT2232C, хотя на самом деле он CH552T. Пара VID/PID у них совпадает.

Подключение следующее:

Функция JTAG

RV-Debugger-Lite

Sipeed Longan Nano

TDO

TDO

JTDO

TDI

TDI

JTDI

TCK

TCK

JTCK

TMS

TMS

JTMS

3V3

3V3

GND

GND

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

Литература

PIO (platformio) JTAG FT232H ESP32

Бюджетный отладчик к ESP-32 и его настройка

Debugging Longan Nano

Подробнее..

Recovery mode Проект электронного мультитула QUARK

19.04.2021 06:22:01 | Автор: admin

Еще в 2018 наткнулся на довольно известный в своих кругах проект Пультоскоп. Если кратко, то это крайне примитивный осциллограф, построенный на arduino 328 серии. Учитывая его простоту, я его повторил за несколько часов на макетке и тут меня понесло... Но обо всем по порядку.

Итак. Повторенное устройство оказалось настолько нужным и удобным, что сама-собой возникла идея дополнить его базовыми функциями мультиметра, в том числе измеритель емкости конденсаторов и индуктивности катушек. В результате я начал работу над созданием идеального "под себя" устройства. Эмпирически прикинув функционал, который так или иначе задействую при проектировании своих устройств, я исключил ненужные мне функции и определил обязательные. В первую очередь, исходил из того, что в большинстве своем я не использую напряжения свыше 24 вольт и токи свыше 3 ампер. Обычно это низковольтное оборудование, IoT, ESP32, arduino и схожие по идеологии устройства. Соответственно и при измерении сопротивлений, емкостей и индуктивностей важна не столько точность, сколько понимание номинала и, желательно, автоматическое определение цветовой и кодовой маркировки. Обычно измерение этих параметров требуется при проектировании питающих схем устройств. Обязательна хотя бы минимальная проверка наличия данных на порту UART, а в идеале и их чтение. Здесь же я стал размышлять над формфактором устройства.

Собственно список того, к чему я пришел в итоге:

  • Вольтметр с точностью измерения не выше 0.01 вольта. Обычно достаточно даже десятых долей. При этом, обязательно необходимо отображение значений логических уровней для CMOS1.8, TTL и CMOS5.0 вольт.

  • Амперметр до 3 ампер с возможностью отображать график изменения значений.

  • Измерение номиналов пассивных элементов. Одна из наиболее необходимых для меня вещей, оказалась возможность на лету отображать цветовую и SMD маркировку деталей. То же самое при измерении индуктивности и емкости, за исключением цветовой маркировки. Разумеется автовыбор диапазонов.

  • Осциллограф обязателен. Как правило используется для измерения ШИМ сигналов, при проектировании питающих частей схем и при работе с данными для АЦП/ЦАП. Частоты как правило не выше 100кГц. Желательно что бы устройство само определяло триггер и настраивало масштаб графика.

  • UART логгер с автоопределением скорости.

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

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

Звучит слишком идеально, но это хотя бы обозначенная цель.

ПРОЕКТИРОВАНИЕ

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

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

Что касается железной части, то здесь пришлось сильно поломать голову. Первые версии были построены на ATMEGA32U4. Его выбрал из-за в встроенного USB и достаточной скорости. Но самым главным критерием была arduino совместимость. Я принципиально хотел использовать платформу arduino, поскольку сразу зародилась мысль выложить ПО в открытый доступ и при этом оно должно было быть понятным ардуинщику. В качестве датчика тока решил использовать ACS712. Остальное на резистивных делителях, там ничего интересного.

Одна из последних тестовых версий плат, собранных на коленке:

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

А это уже первый заводской прототип:

Виден не распаянный в верхней части блютуз модуль JDY-08. Использовал его потому, что был. В качестве встроенного зонда, использовал иглы для тестирования плат методом летающего щупа. Сам зонд выглядит так:

Игла немного подпружинена и не дает поцарапать медь или лак на плате.

Косяков было не так много, все довольно легко смог исправить кинув "сполю", но пришлось отложить проектирование из-за нехватки времени. Собственно это позитивно сказалось на проекте, поскольку удалось много поработать с ESP32, на него я и перевел устройство. Попутно заменил старый, 0,96 дюймовый дисплей с разрешением 80x160, на 1,14 дюймовую матрицу 135*240 пикселей.

Должен сказать, что я не сторонник сенсорных кнопок, и в первой версии устройства у меня были физически нажимаемые 3 кнопки, но пришлось себе изменить. Шутки ради собрал прототип с 3-х контактным сенсорным интерфейсом и воткнул его в отпечатанный на 3D принтере корпус. Оказалось, что проще и быстрее не искать пальцем нужную кнопку, а банальными свайпами переходить в нужный режим. Сразу возник соблазн использовать встроенные в ESP32 пины емкостного сенсора, однако их чувствительности оказалось недостаточно для работы в корпусе. Потому пришлось перейти на использование букашек от ttp223. Из-за катастрофической нехватки пинов, три сенсора я подключил через резистивный делитель на один аналоговый вход. Еще один пришлось вешать на отдельный пин для реализации выхода из сна по прерыванию. С аналоговым входом, на котором висят 3 сенсора возникла неожиданная проблема. Оказалось, что при включении режима bluetooth или WiFi , некоторые пины не могут читать аналоговые данные. На практике, при подключении к смартфону, устройство просто переставало реагировать на сенсоры. И все бы ничего если бы я хотя бы мог переразвести, но функции были раскиданы по пинам с учетом их специфики, и получалось, что любой пин к которому можно подключить сенсоры, оказывался именно тем, который не мог работать при включении радиомодуля. Но интернеты, таки помогли.

Немного пораскинув мозгами, пришел к выводу, что вместо ACS712, лучше использовать INA219. Во-первых управление по I2С, во-вторых возможность измерять напряжение до 26 вольт. При измерении сопротивления, обычно используют коммутируемые резистивные делители для разных диапазонов, эта же функция необходима при измерении ёмкости конденсаторов. Вариант использовать ограниченное число пинов для коммутации резисторов такой себе, вместо этого я поставил цифровой потенциометр на 100К AD5245. Таких в устройстве два. Второй регулирует чувствительность ОУ на входе. Второй щуп перенес в разъем для зарядки TYPE-C и несколько видоизменил корпус.

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

Ниже небольшой видеообзор:

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

На сегодняшний день, у меня написан основной базис ПО. Пришлось повозиться с корпусом, зато смог максимально удешевить стоимость пресс-формы. Для понимания порядка цен, стоимость матрицы $5600, ресурс 300к отливок. Стоимость 1 отливки, включая заглушку на дисплей и подсветку рабочей зоны $1,53. Размеры ~120x22мм. Корпус цельнолитой из ABS с SoftTouch покрытием.

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

Подробнее..

Кикбрик фитнес-трекер для ударных видов спорта

17.05.2021 16:18:04 | Автор: admin

История инди-разработчика без бэкграунда, о том как упорно идти к мечте с 1 сентября 2016г. Я делаю фитнес-трекер для ударных видов спорта "Кикбрик". Гаджет превращает боксерский мешок в умный, добавляя возможности для тренировки. Тренировка скорости реакции, комбинаций ударов, темпа ударов на время, интервальные тренировки.

Деритесь в Подольске, заказывайте производство в Шеньжене!

Видео на английском

Сколько проходит времени между твоим желанием ударить и самим ударом? Сколько ударов ты максимум можешь совершить за 1 минуту? Как долго ты сможешь держать темп один маваши в секунду? То, что нельзя измерить, нельзя улучшить!

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

История

Ранее я писал о том как появилась идея http://personeltest.ru/aways/habr.com/ru/post/397107/

Как с помощью сообщества удалось ее улучшить http://personeltest.ru/aways/habr.com/ru/post/397191/

Какие конкуренты есть у Кикбрика http://personeltest.ru/aways/habr.com/ru/post/398441/

Представляю Вам фитнес-трекер для ударных видов спорта. Гаджет подходит для измерения и тренировки скорости реакции, темпа ударов. Совершаем удар после получения сигнала, видим время нашей реакции, тренируемся для улучшения. Измеряем и увеличиваем количество ударов за определенное время. Режим Метронома для удержания темпа ударов. Режим тренировки комбинации ударов. "Кикбрик" показывает общее время Вашей тренировки, общее количество ударов.

Условно можно сказать, что начал разработку 1 сентября 2016г. Так выглядел первый прототип, собранный за 20 минут.

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

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

Следующая степень миниатюризации

Мы сделали проект в EasyEda и заказали прозводство и smd монтаж в jlcpcb. Идея была в том, что в центре всего приложение. Приложение это некая экосистема, где тренеры могут размещать свои видеоуроки по отработке определенных ударов и к ним программу тренировок с кикбрик. Гаджет в данном случае использовался для передачи сырых данных об ускорении и углах в приложение. Сама плата на TI CC2640 c BTLE.

Мы общими усилиями собрали приложение и устройство. Корпус напечатали.

Видео демонстрация для Хабиба. Мои первые слова на аварском.

В каком-то виде все работало, но было стоимость пресс-форм удручало, а до требуемой компактности было очень далеко. И тут мы нашли устройство, которой было очень похоже на то, что нам нужно, содержало на борту дисплей, динамик, кнопку, аккумулятор и ESP32, все в отличном корпусе за 3000 руб.

Были два элемента, которые нас не устраивали в самой плате, мы договорились с производителем о смене гироскопа на MPU6050 и смене ножек подключения. За изменение крепления с меня попросили еще $1200, это решил делать сам.

Прошивку и дизайн я разрабатывал сам. Использовал для графики LVGL.

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

Заказ опытной партии в 100шт в Шеньжень

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

После изготовления PCB и автоматизированного SMD можнтажа идет этап монтажа навесных элементов.

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

Загрузка тестового скрипта для проверки всей периферии.

Готовая плата с элементами размещается в корпус.

Готовые устройства складываются в боксы для дальнейшего тестирования и упаковки.

Процесс зарядки и финального тестирования.

В конце процесса упаковка в индивидуальные боксы и упаковка боксов в общую коробку.

Первая партия в 100шт.

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

Устройство опробовали в секциях каратэ, бокса, кикбоксинга, муай тай, рукопашного боя, мма и тхэквондо. Гаджет отлично подходит для самостоятельной работы с мешком, мотивирует работать дольше и эффективнее. Профессионалы оценили тренировку скорости реакции и темпа. Начинающие и любители получают мотивацию работать с мешком дольше. Особенно в восторге от гаджета дети и подростки. Зарядки хватает на 3-4 тренировки. Есть аналоги датчики под бинты, которые стоят от 300$. Они не тренируют реакцию и темп. Не все используют бинты. Существуют умные груши, которые обладают меньшим функционалом, но стоят более 1200 долларов. Их неудобно заряжать, брать с собой в зал или домой.

Проблема сохранения результатов

Сейчас результаты отображаются пока Вы тренируетесь. В следующий раз посмотреть Ваш прогресс или прошлый результат нельзя. Так как на борту esp32 - то есть возможность отправлять данные по bluetooth или wifi для дальнейшего хранения и обработки либо на телефоне в приложении, либо сразу на сервер с общим рейтингом и личными кабинетами.

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

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

Экономика

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

100 устройств с кастомизацией - 320.000 руб.

Доставка и таможенное оформление - 45.000 руб.

Печать и литье в силикон креплений - 5.000 руб.

Карабин, стрейч-лента - 2.000 руб.

Печать этикеток - 2.000 руб.

Затраты 3740 руб./устройство.

Если добавить к себестоимости планируемую прибыль в 1160 руб. - получаем стоимость устройства в 4.900 руб. Почему 1160? Чтобы получилась красивая цифра и именно столько стоят 2 похода в зал мне. Т.е., покупая, гаджет каждый еще и меня мотивировать будет.

Если Вы хотите принять участие в проекте, есть идея или хотите для себя приобрести гаджет, пишите мне в WhatsApp или Telegram+79267031561.

Продвижение

Продемонстрировал гаджет на мероприятии "Инновации в спортивной индустрии" на Красном Октября .

Я вступил в Московский инновационный кластер. Сейчас подал заявку на пилотирование с сетью клубов "Ударник".

Подал заявку на участие в ежегодном конкурсе инновационных проектов премии Мэра Новатор Москвы. Результаты ожидаем 25.05.2021

Вариантов продвижения я вижу несколько:

  • продажа через свой сайт, реклама у тематических блогеров

  • партнерство с известным спортсменом

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

  • выход на кикстартер

  • продажа производителю спортивного оборудования

Есть ли у Вас идеи? Какие тренировки можно было бы ещё делать? Как продавать? Как позиционировать? Может есть знакомые, которые имеют опыт краудфандинга?

Подробнее..

Проект электронного мультитула QUARK. Часть 2

11.06.2021 16:11:37 | Автор: admin

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

QUARKQUARK


Для начала напомню, что собой представляет устройство. QUARK это электронный мультиинструмент, в первую очередь, ориентированный под разработку микроконтроллерных устройств, Arduino, ESP32, STM32, IoT, домашняя автоматизация и тому подобные девайсы.

Че определяется такая ориентированность? Во-первых, набором функций. И если первая часть стандартна для типичного мультиметра:

Вольтметр

Амперметр

Измерение сопротивления

Измерение емкости

Измерение индуктивности

то UART логгер и UART плоттер вещи необходимые ардуинщикам и иже с ними. Осциллограф конечно не блещет скоростью 400 килосемплов, однако проверки ШИМ сигналов и сигналов с различных датчиков, его более чем достаточно.
Во-вторых идеологией работы с выводимыми значениями. Скажем, ардуинщику практически никогда не требуется знать напряжение с точностью в одну тысячную процента, зато значение логического уровня параметр всегда необходимый:

ВольтметерВольтметер

Точно так же мгновенное потребление тока, при разработке IoT, параметр в себе. Гораздо удобнее проследить изменение в динамике при различных режимах работы устройства:

АмперметерАмперметер

Калькулятор цветовой и СМД маркировки компонентов вещь полезная и нужная, но, согласитесь, так удобнее:

СопротивлениеСопротивление

А сейчас опишу свой личный опыт работы с осциллографом. В свое время приобрел себе DS203. Вполне себе, по характеристикам, годный девайс, с жутко неудобным управлением:

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

ОсциллографОсциллограф

UART данные, конечно, удобнее смотреть на телефоне чем на дисплее устройства. Не скажу, что я прям мучаюсь при подключении UART-TTL моста к компу, но в поле не всегда имеется такая возможность:

Теперь пару слов о схемотехнике. Вся система построена на ESP32 со всеми вытекающими из нее ништяками (bluetooth, Wi_fi). Для измерения напряжения и тока я использовал готовый чип INA219. Подключается по I2C шине, имеет малый размер и вполне достойные, для моих задач, параметры. Сопротивление измеряю стандартным делителем напряжения, но в качестве известного сопротивления использую цифровой потенциометр AD5245, что освобождает пины контроллера, а учитывая тот факт, что AD5245 управляется по I2C, так и вообще нет нужды в дополнительных пинах. Тем же способом измеряю емкость конденсаторов по известному методу заряда до 63.2%. На больших емкостях AD5245, подключенное к питанию имеет низкое сопротивление, а при низких, заряд идет через 1 мегаомный резистор. Таким образом, минимальная измеряемая емкость определяется пикофарадами.
Индуктивность меряю резонансным методом при известной ёмкости по срабатыванию компаратора.
Тракт осциллографа реализован на Rail-to-Rail ОУ AD8541, усиление сигнала регулируется вторым AD5245. Соответственно, сигналы как с высоким, так и с низким размахом амплитуды, поступают на вход АЦП в максимальном разрешении. Для оцифровки использую аппаратный I2S, складываю весь буфер в DMA и вывожу на LCD и, при необходимости, отсылаю по bluetooth.

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


А теперь хотелось бы сказать пару слов о тех нововведениях, которые были реализованы на основе предложений к предыдущей статье.

Во-первых, универсальный прикручиваемый щуп:

Такие будут как в корпусе, так и во втором щупе. Довольно универсальное решение на стандартной резьбе М2.
Во-вторых, расширен функционал: поиск компонентов по заранее введенному значению и удержание значений с записью в лог.
И в-третьих, вариант без корпуса, этакий кит-набор, но распаянный.

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

Подробнее..

Концепция независимой инфраструктуры для IIoT системы на основе mesh cети

10.12.2020 22:22:03 | Автор: admin

Добрый день,

Меня зовут Алексей Бабушкин. Я СЕО независимого дизайн хауса электроники Hi-tech nation. Мы занимаемся контрактной разработкой продуктов в области интернета вещей. В свободное от работы время пилим свои решения с использованием беспроводной передачи данных и тестируем продуктовые гипотезы. Так родилась концепция независимой инфраструктуры для IIoT систем на основе mesh сети, которой я хочу с вами поделиться.

Начнём с теории

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

Типовая архитектура таких систем состоит из следующих 3-х уровней:

- оконечные периферийные устройства (датчики, контроллеры, исполнительные устройства и пр.). Этот уровень отвечает за сбор информации с устройств и за приведение её к стандартному виду, фильтрацию и локальное хранение;

- сетевые шлюзы (роутеры, gateway станции и пр.). Они создают инфраструктуру для управления и обмена данными между устройствами посредством проводных или беспроводных протоколов (RS-485, Modbus, CAN, BLE, Wi-Fi, ZigBee, LoRa, и пр.). На этом уровне происходит предварительная обработка информации, выстраивается коммуникация с верхнем уровнем и предоставляется возможность настройки и управления через веб-приложения;

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

Теперь немного глубже

На старте формирования нашей концепции мы решили проанализировать все три уровня и выделить их слабые стороны.

На первом уровне главной головной болью является вопрос нахождения баланса между вычислительными возможностями оконечных устройств, их роли в работе всей системы и энергопотреблением. Современные сложные IIoT системы требуют большей автономности работы (в отрыве от сервера), быстрой реакции на команды (в части исполнительных устройств), дополнительной обработки информации, принятия решений на местах и простоты установки и настройки (желательно по принципу plug and play). При этом никто не отменял запрос на более низкую цену из-за большого количества подключенных устройств.

Слабые стороны второго уровня связаны, в первую очередь, с набирающим обороты переходом на беспроводную передачу данных. Радиоэфир становится все более перегруженным, поскольку большинство нелицензированных беспроводных технологий основаны на частоте 2,4 ГГц. Вскоре это станет одним из самых ограниченных ресурсов. Также стоит отметить неблагоприятную среду, в условиях реально работающих промышленных объектов, в которой приходится функционировать IIoT (большое количество металлоконструкций, высокочастотные помехи от работы производственного оборудования, Wi-Fi сети и пр.). Все эти факторы в совокупности могут привести к снижению качества работы IIoT системы или даже к полной остановке её работы. Чтобы избежать проблем со связью в будущем, современные беспроводные решения должны уметь сосуществовать с другими беспроводными технологиями, поддерживать динамическую смену каналов и работать на других частотах, например, 433 или 868 МГц.

Что касается непосредственно сетевых шлюзов, то сегодня многие производители устанавливают в свое оборудование, например, в те же роутеры или gateway-станции, как можно более высокопроизводительное "железо". Делается это не для улучшения качество сигнала, а чтобы предоставить пользователю, дополнительный функционал управления, со сложными графическими интерфейсами, выводом статистики и т.д. В первую очередь они делают это для того, чтобы оправдать завышенную стоимость новинок. Во вторую с целью улучшения пользовательского опыта при взаимодействии с продуктом. Понятно, что подобные действия продиктованы бизнесом. Возникает вопрос: зачем такое удорожание конечному пользователю. Такой подход противоречит основному принципу концепции промышленного интернета вещей, который гласит: стоимость решения не должна превышать ценность, которую пользователь получает в результате внедрения. Соответственно в интересах пользователей себестоимость отдельных компонентов должна не увеличиваться, а наоборот снижаться, как и стоимость обслуживания.

Слабые стороны третьего уровня связаны в первую очередь с недостатками облачных решений. К таким можно отнести вопросы безопасности. Согласно исследованию Crowd Research, проведенному в 2019 году, около 90% специалистов по кибербезопасности обеспокоены безопасностью используемых облачных сервисов. В некоторых отраслях (например, оборонно-промышленный комплекс) службы безопасности на основе внутренних нормативных актов ограничивают передачу данных за периметр предприятия, что исключает возможность использования облачных решений в принципе. Ещё одним недостатком облачных сервисов считают большую степень зависимости от поставщиков услуг (так называемый vendor lock-in), которые помимо предоставления сервисов и инфраструктуры, потенциально получают неконтролируемое влияние на рынок и клиентов. Так же сюда можно отнести высокую стоимость предоставления подобного рода услуг.

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

Так исторически сложилось

Все периферийные устройства мы, как правило, разрабатываем на основе микроконтроллера ESP32 разработанного компанией Espressif Systems. На наш взгляд, сегодня это один из лучших вариантов с точки зрения цена/качество/производительность на рынке. За вполне приемлемую цену мы получаем два ядра, аппаратные блоки шифрования, интегрированныеWi-FiиBluetooth модули, практически все периферийные интерфейсы и встроенный сопроцессор, на котором, посредством программирования на Assembler, можно весьма эффективно решать различные фоновые задачи в режиме ультранизкого потребления. Пришлось, конечно, практически полностью переписать китайский SDK, но сейчас не об этом.

Mesh

Беспроводную передачу данных мы решили строить на основе разработанной нашими инженерами mesh-cети. Q-mesh это сетевой протокол, построенный поверх протоколов IEEE 802.11n, IEEE 802.11lr и Bluetooth, позволяющий объединять многочисленные устройства, распределенные по большой площади как внутри, так и снаружи помещения в единую беспроводную локальную сеть. Сеть работает на частотах 2.4 ГГц и 868 МГц или 433 МГц. Частота 2.4 ГГц используется как основная, а частота 868 МГц или 433 МГц как дополнительная, для повышения надёжности, а также для маршрутизации между удаленными сегментами сети.

Наше решение отличается от аналогичных беспроводных решений тем, что не требует применения выделенной gateway станции. Любое из оконечных устройств, имеющих гарантированное питание, может полноценно взять на себя её функции. Выбор такого устройства происходит автоматически. Как правило, им становится то, у которого лучше условия обмена информацией. Например, самый низкий уровень помех, наибольшее число доступных устройств и т.д. Если устройство, выступающее в роли ведущего, по каким-либо причинам выходит из строя, то в течение нескольких секунд будет назначено другое. Замена происходит незаметно для пользователя.

Как мы храним данные

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

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

В процессе работы такой системы может генерироваться значительный объем данных. Для их хранения, обработки и маршрутизации в системе предусмотрены сервера, построенные на микро сервисной архитектуре, которая позволяет распределять нагрузки. Это делает систему более устойчивой и надежной. Сервера обеспечивают передачу данных между удаленными ячейками mesh-сети, синхронизируют данные между собой и обновляют данные у пользователей в реальном времени. Устройства и пользователи подключаются к системе через веб-сокеты, что обеспечивает быстрый отклик устройств на команды и быстрое предоставление свежих данных пользователям.

Автономное питание

Все вычисления осуществляются внутри системы распределенным способом, в зависимости от источника питания. Что имеется в виду? Если одному из устройств с автономным питанием необходимо произвести сложные и энергозатратные вычисления, то система перенаправит эту задачу ближайшему устройству, имеющему постоянное сетевое питание. Такой подход обеспечивает приемлемый срок автономной работы для устройств с автономным питанием и ускоряет процесс принятия решения.

Элементы такой системы с автономным питанием рассчитаны на длительную работу без обслуживания. С целью экономии энергии эти устройства большую часть времени проводят в спящем режиме и, соответственно, не имеют постоянной доступности. Для эффективной работы с такими устройствами предусмотрена их виртуализация. Реализуется она следующим образом: на одном из ближайших, в смысле топологии и доступности, устройств, имеющем постоянное питание, создается виртуальная копия реального устройства и вся работа других устройств системы ведется уже с ним. Реальное же устройство по собственной инициативе общается со своим двойником и синхронизирует его состояние с реальным. Таким образом, например, пользователю нет необходимости ждать, когда проснется удаленный датчик, работающий по технологии NB-IoT, чтобы передать ему новые настройки. Он просто настраивает его цифрового двойника, и все настройки будут загружены по назначению во время очередного сеанса связи.

Ледовая арена Химик. Управление освещением реализовано согласно нашей концепцииЛедовая арена Химик. Управление освещением реализовано согласно нашей концепции

Передача данных

В отличие от традиционных сетей, например, Wi-Fi, Q-mesh поддерживает динамический выбор канала, основываясь на информации о загрузке каналов, интенсивности и спектре помех. Это способствует более устойчивой работе системы в условиях загруженного радиоэфира.

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

Скорость передачи данных в диапазоне 2.4 ГГц составляет от 0.25 до 112 Мбит/сек, а время задержки передачи от точки к точке от 5 до 50 миллисекунд. Скорость передачи данных в диапазоне 868 МГц составляет 1 Мбит/сек. Передача данных в диапазоне 433 МГц происходит на скорости 0.25 Мбит/сек.

Безопасность сети обеспечивается применением современных алгоритмов шифрования (AES с длиной ключа 192 или 256 бит) с использованием аппаратной поддержки. Трафик туннелируется между сервером и устройством или между парой устройств и шифруется отдельным ключом.Ключ генерируется из шума радиоэфира.

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

Вся система после включения настраивается автоматически, а в процессе работы перестраивается и подстраивается под внешнюю обстановку: расположение соседних устройств, уровень сигнала, загрузка радиоэфира. Процесс первоначальной настройки занимает от 3 до 60 секунд в зависимости от размера сети и заданной конфигурации. При выходе одного или нескольких устройств из строя перестроение системы происходит за время до 10 секунд.

Управление и настройка такой системы происходит через веб-интерфейс, который может раздавать любое из устройств системы выполняющего роль сетевого шлюза (gateway станции). Учитывая ограниченные вычислительные возможности периферийных устройств, поддержку веб-интерфейса они отдают в виде скрипта на сторону браузера смартфона, ПК или планшета, которые, располагают куда большей производительностью. Так как это происходит только в момент, когда пользователь взаимодействует с системой, то нагрузки на принимающую сторону не значительны. В свою очередь, для того, чтобы устройство проснулось и передало необходимые данные дальше, ему также не нужны большие вычислительные ресурсы. Поэтому мы можем использовать более дешевые и менее производительные микроконтроллеры, создавая для пользователя дополнительную ценность. В дополнение к этому, решения для браузеров легко интегрировать, так как они работают практически с любой ОС: Linux, Mac, Android и т.п. Таким образом, на том же ESP32 с 256 Кбайт оперативной памяти, мы можем запускать веб-приложения практически, любой сложности и с любой графикой, производить сложные вычисления на стороне веб-приложения с приемлемой скоростью отображения. Это возможно потому, что основная обработка происходит на стороне значительно более производительного оборудования и, поэтому, запас вычислительных ресурсов у нас практически безграничный.

Резюме

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

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

Подробнее..

Умный дозатор таблеток или мой первый опыт в IoT

22.01.2021 16:14:24 | Автор: admin

Автоматический дозатор


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


Немного про IT Академию Samsung


Перед тем, как рассказать про мою разработку, хочу поделиться впечатлениями от первых занятий в IT Академии Samsung по треку Интернет вещей, который я прошел в НГТУ в прошлом учебном году. Началось всё с прошивки контроллера STM32 RIOT OSИ всё это через Linux и командную строку. Честно скажу, что опыт незабываемый настолько, что я уже начал переживать, что в 2020 году люди все еще программируют в командной строке и после компиляции заливают образ вручную на контроллер. Но уже на следующем занятии нас познакомили с инструментами, которыми пользуются разработчики, а всё предыдущее было для демонстрации того, что происходит, когда мы нажимаем волшебную кнопку Build в среде разработки. Хочу также отметить, что за весь курс мы ни разу не программировали с помощью фреймворка Arduino, что, как по мне, является огромным плюсом, поскольку в реальных задачах и на производствах данный фреймворк (я надеюсь) не используется. Нас познакомили с Mbed, RTOS, а также ESP-IDF (которая, к слову, базируется на FreeRTOS). Мне нравилось, что каждый кейс начинался с объяснения, для чего будет использоваться данное устройство, и какие технологии уместно применять в нем.


По окончании обучения каждый студент представлял свой проект по Интернету вещей для получения сертификата IT Академии Samsung. Это мог быть простой светильник с функцией включения по Bluetooth, или что-то посложнее Мне хотелось сделать устройство, которое будет сложным с точки зрения программирования, при этом решать действительно важную проблему. Поэтому я остановился на умном дозаторе таблеток.


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


Про разработку


Как же я пришёл к тому устройству, что есть сейчас, получившему название AutoPill?


Шаг 1. Определение функционала и выбор микроконтроллера


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


Итак, что я хотел получить на выходе? Как мне казалось на тот момент, успеть это выполнить к сроку (конец учебного года) вполне реально.


  • Устройство должно быть таким, чтобы пользователь (пациент) не мог по ошибке открыть сектора с хранящимися препаратами. Сектора должны открываться по расписанию автоматически и блокироваться при открытии следующего отсека.
  • Устройство должно сигнализировать пользователю о необходимости принять новую порцию препаратов.
  • Отсеков должно хватать более чем на один день.
  • Конструкция устройства должна быть компактной (в качестве примера большинство приводили шкатулки или небольшую коробку).
  • Гибкая настройка расписания: возможность задать периодичность с шагом в 30 минут.
  • Возможность посмотреть, принял пользователь препараты или нет.

И необходимо проработать следующие особенности:


  • Как открывать/закрывать отсеки?
  • Как понять, когда пользователь принял препараты, чтобы отключить индикацию?
  • Как сообщать пользователю состояние?
  • Как настраивать устройство?

Начнём с самого очевидного: для настройки и передачи состояния таблетницы было предварительно решено использовать Bluetooth или Wi-Fi, поскольку у большинства людей в доме есть устройство, поддерживающее тот или иной протокол.


Чтобы понять всё остальное, необходимо было разработать конструкцию таблетницы. На этом этапе я также выбрал микроконтроллер ESP32, поскольку у него есть множество встроенных возможностей, которые позволят в дальнейшем значительно расширить платформу (например, ESP-NOW, ESP-Touch и ESP-Mesh), а бонусом была поддержка двух необходимых протоколов связи WiFi и Bluetooth.


Шаг 2. Конструкция устройства


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


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

    Aidapt Deluxe VM930AB
  2. Карманная таблетка этот тип я назвал так, потому что он округлой формы, с отсеками, расположенными по кругу. В центре есть место для таймера, который может сигнализировать о необходимости принятия лекарств
    Карманная "таблетка"

    Bradex недельная с таймером KZ 0439

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


Какие дозаторы были найдены


Я принял решение разработать свой корпус устройства, не забывая и о функциональности, обозначенной в шаге 1.


Для того, чтобы разработать конструкцию, было бы неплохо выучить какой-нибудь САПР. К счастью, в лицее я научился работать в Компас-3D, что сильно упростило задачу. Вторая проблема изучить, из каких материалов можно сделать из 3D модели настоящий прототип. Мой выбор пал на 3D-принтеры, поскольку это, по моему мнению, самая простая возможность создать прототип и напечатать деталь практически любой сложности.


Итак, представляю самую первую версию сборки из, на тот момент, трёх деталей


Инженерам не открывать!

1я версия устройства


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


  1. Не поленитесь посмотреть размеры столов самых популярных принтеров, чтобы у вас был широкий выбор, где заказать печать или попросить по дружбе
  2. При проектировании, если вы раньше ни разу не видели принтеры вживую (как я, например), посмотрите, как эта технология реализована, и какие детали ей сложнее всего печатать, постарайтесь оптимизировать модель под более простую печать для уменьшения времени печати и количества брака. Наглядный пример, как делать не надо, можете посмотреть на первую версию крышки устройства. У неё очень пологий угол наклона, что делает печать без поддержек невозможной, и даже с поддержками шанс брака очень велик, так как пластик при такой толщине может просто не сцепиться.
  3. Не печатайте на плохо откалиброванных принтерах или принтерах с низкой точностью печати, особенно это касается конструктивно важных деталей (шестерни). Тоже немного негативного опыта я получил при печати основы на дешёвом принтере: вышло нарушение соосности деталей, что создало множество проблем, начиная с неравномерного трения при разных положениях отсеков, и заканчивая длительной подгонкой деталей.

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


Сборка последней версии


На данной иллюстративной сборке не видно таких элементов, как:


  • нижняя крышка основы, в которой расположен редуктор и посадочные места для всех электронных компонентов;
  • шестерня (к сожалению, пришлось просчитывать свою шестерню для плоскоцилиндрической передачи), в остальном редукторе используются шестерни из первого попавшегося набора шестерёнок для электроконструктора.

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


Шаг 3. Электроника


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


  1. Микроконтроллер. В моём случае я взял ESP32 Dev Kit с уже распаянным на плате понижающим регулятором напряжения, а также отладчиком.
    ESP32 Dev Kit
    ESP32 Dev Kit
  2. DC мотор мотор коллекторного типа, наверное, один из самых популярных, используется в радиоигрушках.
    DC двигатель
    DC двигатель
    Рабочее напряжение: 3 Вольт ~ 8 Вольт
    Ток потребления при напряжении 3,6В: 240 мА
    Тип двигателя: коллекторный
    Вес: 26 гр
  3. Щелевой датчик датчик, необходимый для отслеживания количество оборотов редуктора. В устройстве стоит датчик с уже распаянным компаратором LM393 (на изображении приведён датчик непосредственно с платой развязки).
    Щелевой датчик ITR9608
    Щелевой датчик
    Рабочее напряжение: 3.3 Вольт ~5.0 Вольт
    Ток потребления энкодера: 1.4 мА
    Ширина паза в щелевом датчике: 5 мм
    Вес: 8 гр
    Рабочая температура: от 0 до +70
  4. Датчик наклона датчик необходим, чтобы определять, взял ли пациент лекарство из устройства. Поскольку отсеки глубокие и достать пальцем сложно, предполагается что пользователь будет вынимать необходимые препараты, наклоняя таблетницу.
    Датчик наклона SW-200D
    Датчик наклона SW-200D
    Рабочее напряжение: до 12 В
    Потребляемый ток: до 20 мА
    Время отклика: 2 мс
    Время жизни: 100000 циклов
    Размер: 12 x 3,6 мм
    Вес: менее 1 гр
    Рабочая температура: от -40 до +80

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


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


Шаг 4. ПО


Хочется отметить, что, конечно, к шагам 2-4 после первой итерации я возвращался не раз, т.к. шла и доработка конструкции, и создание ПО. И хочется уточнить, что шаги ни в коем случае не выполнялись одновременно, а именно доводились до логического завершения, потому что ПО сильно зависело от текущей конструкции.


Самый первый вопрос разработчика ПО для IoT будет следующим (по крайней мере для меня): существует ли IDE для разработки устройств IoT? Почти для каждого микроконтроллера существует своя IDE, но лично я пользовался универсальной средой PlatofrmIO. В течение всего цикла разработки я ни разу не пожалел об этом решении.


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


Язык, на котором написан код проекта: C. Честно сказать, после нескольких лет написания кода на языках Java, Kotlin и иногда C#, я, хоть и был морально готов к этому испытанию, но не ожидал, что встречу столько неудобств. Но в итоге это оказался очень классный опыт.


Прежде чем приступить к разработке ПО, я изучил на просторах GitHub, как вообще пишут программы под ESP-IDF, и, к моему большому удивлению, результатов (если не считать библиотеки с примерами) оказалось крайне мало. Ладно, подумал я, и решил разработать свою архитектуру для ПО.


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


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


После того, как мы разобрались с архитектурой, приступаем к планированию, в каком порядке мы будем реализовывать наши модули:


  1. Работа с периферией. Самый очевидный модуль, причём я принял решение разработать отдельные модули для отображения и для входных данных. Как впоследствии оказалось, достаточно было сделать только тот, что для входа.
  2. Подключение устройства к Wi-Fi. Я решил именно подключаться к Wi-Fi и разворачивать свой мини-сервер, поскольку станций Интернета вещей не было ни у кого из опрошенных, а Wi-Fi есть дома всегда. Подключение производилось через WPS, с возможностью изменения в настройках.
  3. Создание HTTP-сервера на устройстве. Очевидно, что для такого сегмента использование протокола HTTP неудачное решение, но на данный момент я его не заменил, поскольку мне важна возможность использования устройства в изолированной сети, что не даёт возможности автоматически продлевать сертификат, а с самоподписанными сертификатами на сервер пропускает разве что Internet Explorer, и то с предупреждениями о том, что это всё мошенничество.
  4. Реализация модуля безопасной авторизации. В данном случае мы говорим о Digest-аутентификации. Данная аутентификация позволяет в пределах локальной сети сделать устройство максимально устойчивым к взлому.
  5. Модуль планировщика. Наверное, самый важный модуль, который должен поворачивать сектор точно при наступлении необходимого времени. При этом, данный модуль должен также поддерживать синхронизацию времени (т.е. перераспределение задач и автоматическое устранение ошибок)

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


Но, приступая к реализации самого, казалось бы, банального, первого модуля для работы с периферией, я столкнулся с объемной и непонятной документацией. Я думаю, это один из основных факторов, почему каких-либо готовых проектов на ESP-IDF мало. К счастью, у разработчиков есть GitHub, который, благодаря примерам, сильно упрощает понимание. Единственное, не забудьте выбрать нужную ветку с вашей версией ESP-IDF! Пожалуй, это единственная проблема данного фреймворка. А в остальном он очень неплох. Внутри него есть практически всё, что раньше вы добавляли с помощью библиотек: WiFi, WPS, HTTP-сервер, NTP, различные функции хэширования и многое другое.


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


Варианты использования устройства


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


Веб-консоль настройки

Веб-консоль


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


Что получилось?


Сам код устройства находится в открытом доступе, и вы можете найти его здесь.


Давайте подытожим.


Для кого разработан AutoPill? Целевая аудитория люди с деменцией и плохой памятью, которые принимают медицинские препараты на постоянной основе. Стоит учитывать, что данное устройство не способно полностью заменить близкого человека или медицинского работника, который выдаёт медицинские препараты, но способно значительно упростить процесс выдачи и контроля принятия лекарств пациентом. Необходимо лишь раз в неделю или реже раскладывать препараты по секторам, а далее удалённо отслеживать принятие препаратов.


Как подключить AutoPill? При первом запуске устройства или смены точки доступа это можно сделать с помощью WPS или протокола ESP-Touch (через приложение на смартфоне).


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


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


Видео о проекте:



Что дальше?


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


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


Владимир Шперлинг

Владимир Шперлинг
vladimir-shperling@yandex.ru
Kotlin, Java, C#-разработчик
Победитель финала конкурса проектов IT Школы Samsung, 2016
Победитель конкурса Школа VR 360, 2018
Победитель конкурса IT Академии в треке Интернет вещей, 2020

Подробнее..

Баг в ESP-IDF MDNS, Wireshark и при чём тут единороги

29.11.2020 22:13:53 | Автор: admin

Всем привет. Я занимаюсь коммерческой разработкой в IoT, в основном мы используем модули от Espressif - ESP8266 и ESP32.

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

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

Разведка

Для начала, я решил собрать максимальное количество данных про ситуацию в её терминальной стадии. Не перезагружая устройство, я запустил Wireshark в режиме Monitor Mode, настроив фильтрацию на MAC-адрес устройства. Выяснилось, что девайс уверен, что его сеть в порядке - он упорно слал роутеру какие-то данные, однако роутер ему ничего не отвечал. Хм, подозрительно.

В админке роутера девайс тоже был виден как подключенный. Но почему же нету обратной связи? На этот вопрос я получил ответ, когда решил подключить к этому же роутеру ещё одно устройство (точнее, один из своих devkit-ов), и убрал фильтрацию в Wireshark. Оказывается, у роутера поменялся MAC-адрес! Хм, подозрительно. Поменялся он ровно на один последний бит, при этом вся остальная техника эту подмену осознала, а вот наше устройство - нет, и упорно отсылало данные на старый мак-адрес, который, конечно же, никто уже не слушал.

Ладно, пришло время узнать больше. Перезагружаем роутер. Теоретически, это же должно вернуть его MAC в "обычное" состояние? И действительно, MAC вернулся. Но зависший девайс уже был "в коме", и упорно не хотел ничего делать. Ничего нового мы от него уже не узнаем, так что перезагружаем и его. А заодно, дабы собрать больше данных, пропишем в Wireshark пароль от роутера, чтобы он расшифровал весь трафик.

Коллапс

Тут произошло что-то странное. Сначала девайс, как положено, вернулся в сеть. А дальше началось... Количество сообщений в окне Wireshark начало расти с невероятной скоростью. Впрочем, через несколько минут всё остановилось - роутер снова решил проявить свою альтернативную, отличающуюся на один бит, сущность. Ладно, у нас есть дамп, давайте посмотрим, что это было.

And the winner is... 99% захваченных пакетов были про MDNS. Мы действительно используем его, чтобы телефоны могли найти наши девайсы в локальной сети, и работать с ними даже в условиях отсутствия облака (что иногда случается по разным причинам, начиная с "забыли заплатить за интернет", и заканчивая сбоями у Amazon). Но почему так много? Интервал между сообщениями - десятки миллисекунд, и соотношение "входящих/исходящих" (по отношению к девайсу) примерно одинаковое. Что ж, пора раскуривать.

Последовательность пакетов была следующая:

  1. Девайс регистрируется в multicast-группе MDNS, чтобы иметь возможность получать запросы на обнаружение.

  2. Девайс отправляет collision-query-пакет с запросом ANY на полный адрес своего "сервиса", чтобы узнать, есть ли в сети кто-то, кому он может "помешать".

  3. Роутер перенаправляет collision-query-пакет всем устройствам multicast-группы, включая сам девайс.

  4. Девайс отправляет advertise-пакет с PTR, SRV, TXT и A/AAAA записями multicast-группе MDNS, сообщая, что он появился в сети.

  5. Роутер перенаправляет advertise-пакет всем устройствам multicast-группы, включая сам девайс.

  6. 2-5 пункты начинают повторяться с большой скоростью.

Сразу становится понятно, почему проблема не проявлялась на нашем тестовом окружении в офисе - наши роутеры не перенаправляют multicast самому отправителю. Но почему мы не видели этого поведения на старой прошивке? И действительно, откатив девайс до старой версии, я стабилизировал ситуацию. Начинаем бить в бубен.

Делаем diff. Видим, что немного поменялся код внутренней логики, не влияющий на сетевую часть. Что ж, это можно отмести почти сразу. Ещё поменялся номер версии... Ну да, с 0.9 на 0.10. Не может же это влиять? Или... Ладно, пока оставим эту мысль. Ищем, где отправляются пакеты из MDNS.

Debugger? printf!

Как поступил бы на моём месте любой профессиональный разработчик? Правильно: начал обмазывать логами все места отправки пакетов в файле mdns.c. Поиск быстро привёл меня к функции _mdns_create_probe_packet. Поискав по файлу места, из которых она вызывается (и пройдя по стеку вызовов немного дальше), я остановился на строчке #2917 в функции mdns_parse_packet. В логах это место действительно появлялось очень часто. Подозрение пало на вызов _mdns_check_txt_collision. Тут начала вырисовываться картина происходящего: девайс, получая свой же advertise, находил в нём TXT-запись, и сравнивал его со своим TXT. Но ведь он сам его отправил! Ладно, смотрим код самой функции. Я даже приведу его тут по частям, с описанием происходящего.

size_t data_len = 1;if (len == 1 && service->txt) {  return -1;//we win} else if (len > 1 && !service->txt) {  return 1;//they win} else if (len == 1 && !service->txt) {  return 0;//same}

Тут мы создаём переменную data_len, а так же проверяем наличие TXT-записи в нашем service. Вроде бы всё нормально - тут мы проходим дальше.

mdns_txt_linked_item_t * txt = service->txt;while (txt) {  data_len += 2 + strlen(service->txt->key) + strlen(service->txt->value);  txt = txt->next;}if (len > data_len) {  return 1;//they win} else if (len < data_len) {  return -1;//we win}

Тут мы высчитываем длину буфера, в который должен влезть наш TXT из сервиса, а заодно сравниваем эту длину с тем, что мы получили из эфира.

uint8_t ours[len];uint16_t index = 0;char * tmp;txt = service->txt;while (txt) {  tmp = (char *)malloc(2 + strlen(txt->key) + strlen(txt->value));  if (tmp) {    sprintf(tmp, "%s=%s", txt->key, txt->value);    _mdns_append_string(ours, &index, tmp);    free(tmp);  } else {    HOOK_MALLOC_FAILED;    // continue  }  txt = txt->next;}int ret = memcmp(ours, data, len);if (ret > 0) {  return -1;//we win} else if (ret < 0) {  return 1;//they win}return 0;//same

Ну и тут мы кодируем наш TXT в буфер, размер которого уже просчитали (и проверили), и сравниваем его содержимое с полученным.

Уже нашли ошибку? Я тоже заметил её не сразу, поэтому решил пройтись по коду построчно. Опять же, помог мне с этим printf.

Когда я "упал" на проверке длины, я немного удивился. Но как? Ведь "правильная" длина (которую 10мс назад отправил сам девайс) точно совпадает с "входящей"! Давайте ещё раз взглянем на её подсчёт.

mdns_txt_linked_item_t * txt = service->txt;while (txt) {  data_len += 2 + strlen(service->txt->key) + strlen(service->txt->value);  txt = txt->next;}

Окей, тут явно виден проход по linked-list. Мы берём очередной элемент, берём длину его key и value... Стоп. Его или service->txt? Так, понятно...

Выходит, из-за бага в коде (который, если верить git blame, лежит там уже очень много лет), мы всегда берём длину от первого элемента. Но как это работало всё это время? А вот так: нам повезло. Всё это время TXT-записи, которые мы добавляли, по сумме длин чётко совпадали с длиной первого, умноженного на N. Получается, стоило нам добавить новый элемент, поменять порядок элементов, или изменить длину любого из них - и всё, баг начинал проявляться. Но при чём тут номер версии? Ах да, мы же передаём его в MDNS... Паззл сошёлся.

И что дальше?

Соответствующий issue в ESP-IDF я отправил, в своих рабочих копиях мы это место исправили, и наши девайсы прекратили сводить с ума бедные роутеры.

Но тут встал правильный вопрос: а сколько ещё таких багов может быть в SDK? Проект большой, кода много (а часть ещё и стороннего - в виде submodule). Наверняка есть что-то интересное, странное, ужасное...? То, что не вылезает у нас на тестировании, но может создать нам проблем в случайный момент времени.

Так при чём тут единороги?

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

А поймал ли единорог этот баг?

К сожалению, data flow PVS-Studio не нашёл этой проблемы с linked-list. Но будем справедливы - я тоже обратил внимание на эту ошибку не с первого раза (и даже не с третьей вычитки кода). Андрей говорит, что подобную диагностику добавить можно - а значит, одна из следующих версий может начать ловить такие гейзенбаги.

Подробнее..

Перевод Легенда на ладони создаём крошечный компьютер PDP11

17.02.2021 12:10:47 | Автор: admin
image

Введение


2020 год был странным для всех в мире. Конкретно в моём случае мне пришлось находиться далеко от своего дома в Шанхае, потому что, когда COVID нанёс удар, мы были в Европе, и Китай усложнил процедуру пересечения границ примерно до конца года.

Отсутствие доступа к моей лаборатории и лишь ограниченный доступ к электронному оборудованию сильно сдерживал мои возможности, однако создание новых проектов не исключалось полностью [перевод на Хабре]. После завершения этого проекта я снова попал в проектный вакуум: всё, что у меня было с собой это куча комплектов разработки ESP32 и желание что-нибудь создать.

Поэтому я подумал: почему бы не создать ещё один эмулятор? У меня был подобный опыт, поэтому это не будет слишком сложно, и как только я доберусь до своей лаборатории, то, вероятно, смогу превратить его в ещё одну миниатюрную работающую модель компьютера, похожую на то, что я сделал с крошечным Macintosh Plus [перевод на Хабре]. Но какой компьютер выбрать на сей раз? Меня всегда интересовал PDP11. Хоть у меня никогда его не было (и я даже на нём не работал), в своё время он находился в авангарде компьютерных технологий. Его архитектура набора команд (ISA) повлияла на довольно значительное количество архитектур, появившихся позже, а солидная доля ПО, которое мы используем по сей день, основывается на идеях, впервые реализованных на PDP11.

На случай, если вы не знаете, что такое PDP11: это линейка миникомпьютеров, создававшаяся Digital Equipment Corperation (также известной как Dec или просто Digital), начиная с 1970-х. Линейка компьютеров PDP стала попыткой DEC проникнуть на рынок компьютеров в 60-х. Хотя компьютеры уже существовали, в основном они были крупными мейнфреймами наподобие IBM, используемыми для сверхважных функций типа расчёта зарплаты, ведения бухгалтерии и других финансовых задач бизнеса. Поскольку обычно они стоили очень дорого, для них не было места в научно-исследовательских лабораториях. DEC увидела возможность получения доли рынка при помощи относительно недорогих машин, однако устройства, которые она собиралась продавать, не должны были называться компьютерами, потому что это слово вызывало бы в сознании образы дорогих и недоступных мейнфреймов. Поэтому DEC придумала название Programmable Data Processor (программируемый обработчик данных), или PDP.


Семейство PDP имело довольно долгую жизнь и содержало несколько интересных машин со множеством ISA: от первого 18-битного PDP1 до 12-битного PDP5 и 36-битного PDP10. Из всех этих ISA одной из самых успешных стала PDP11. Линейка PDP11 с 16-битной ISA и ортогональным набором команд на основе регистров использовалась во многих машинах, начиная с первой PDP11/20, созданной в 70-х, до PDP11/94, вышедшей в 90-х. Кроме того, с точки зрения функциональности, у машин было много вариантов использования: PDP11 могли работать с DOS-подобными операционными системами реального времени типа RT-11 или многозадачными ОС типа Unix. Их можно было использовать как однопользовательские машины, многопользовательские серверы и встроенные машины, управляющие механизмами и предоставляющие интерфейс пользователя. Архитектура PDP11 даже добралась до аркадных автоматов: одночиповый вариант PDP11 под названием T11 работал в таких аркадных играх Atari, как Paperboy.

В PDP11 меня привлекло то, что линейка PDP в целом всегда была семейством машин для хакеров. Её представители были достаточно дешёвыми, чтобы люди могли делать с ними всякие забавные штуки, и например первая компьютерная игра, SpaceWar!, была написана на PDP1. Однако она не стала последней, написанной на машине PDP: кроме вышеупомянутых аркадных игр, в далёкой России советский разработчик программ Алексей Пажитнов написал на клоне PDP11 игру Tetris, позже распространившуюся по всему свету.

Благодаря чудесам эмуляции сегодня мы можем использовать историческое ПО PDP11. Один из лучших эмуляторов PDP11 это SIMH, который, похоже, работает на большинстве ОС стандарта POSIX. ESP32 с ESP-IDF имеет довольно приличную совместимость с POSIX, так почему бы не попробовать портировать эмулятор?

Запускаем Tetris


Очевидно, что просто портируем SIMH на ESP32 явное преуменьшение необходимых для проекта усилий. Даже несмотря на то, что SIMH это очень крутая программа, потому что она не использует никаких уникальных API, она всё равно разрабатывалась с расчётом на полнофункциональную рабочую станцию с дешёвой и объёмной ОЗУ. Чтобы заставить работать эмулятор на ESP32 и оставить ещё памяти, которую можно будет использовать как ОЗУ эмулируемой машины, придётся для начала порезать программу.

Основными потерями станут сетевой стек и парсер конфигурации. По сути, SIMH состоит из множества модулей, каждый из которых эмулирует одно или несколько периферийных устройств. Например, каждый обобщённый тип диска имеет свой модуль. То же самое относится к сетевым устройствам, интерфейсам телетайпа, портам принтеров и т.п. Все эти модули соединяются при помощи конфигурации, задаваемой при помощи интерфейса конфигурации, работающего из командной строки. Этот интерфейс конфигурации используется при запуске для сборки нужной пользователю конфигурации PDP11, а также применяется в дальнейшем как интерфейс отладки из командной строки.

Хотя такая модель работы очень удобна и позволяет динамически создавать конфигурации PDP11 и даже менять их на лету, она составляет довольно большую часть кода, и на ESP32 с его ограниченными ресурсами реалистичнее будет жёстко прописать конфигурацию. Сетевой код имеет другую проблему: хоть он и обеспечивает огромное изобилие способов подключений, ни один из них несовместим с тем, что работает в ESP32. Поэтому от этого файла я тоже избавился.

Вырезал я и более мелкие вещи, например, всю необязательную периферию (плёночные приводы, векторные мониторы, считыватели перфокарт и т.д.), чтобы она не занимала места в ОЗУ и во флэш-памяти. Также были урезаны функции отладки: хотя у SIMH есть довольно приятные функции, применимые для отладки любого кода PDP11, в этой версии они нам не понадобятся.

В конечном итоге я изначально собрал голый минимум для того, чтобы запустить Tetris. Конкретно российская версия Tetris запускалась на Электронике-60, клоне PDP11-23, имеющем не более 64 КБ ОЗУ и выполняющем вывод через последовательный порт на терминал 15ИЭ-00-013, который был основан на DEC VT52 с поддержкой кириллицы. В машине работала ОС ФОБОС советский аналог операционной системы RT-11 компании DEC.

Так как конкретно советское железо и ПО в эмуляторе недоступно и не поддерживается, лучшее, что я мог сделать использовать оригинальную западную систему, с которой всё это было скопировано: похоже, русские провели хорошую работу по сохранению перекрёстной совместимости. Поэтому ESP32 был сконфигурирован для эмуляции PDP11-23 с 256 КБ ОЗУ и приводом гибких дисков RX01, что давало мне 256 КБ дискового пространства под операционную систему и файлы игры. При помощи SIMH я на своём ноутбуке создал пустой диск и установил на него RT11. Затем я взял риск с русскими играми, на котором был Tetris, и скопировал двоичный файл. Этот образ диска будет прошит вместе с эмулятором на ESP32. Пока я решил не заморачиваться с терминалом, выбрав вывод консоли PDP11 на отладочный последовательный порт ESP32.

Это позволило мне запустить RT11 и получить доступ к командной строке. Однако запуск Tetris не принёс мне ничего полезного: игра ожидает советский терминал 15ИЭ, а мой современный эмулятор терминала даже не пытается его эмулировать.

К счастью, этот терминал эмулировали в рамках проекта Mame/MESS. Найдя нужные ROM, я смог запустить эмуляцию этой древности, и благодаря возне с конфигурацией и socat мне наконец удалось увидеть оригинальный Tetris, впервые запущенный на ESP32:


Очевидно, что такое решение не особо интересно; нам бы хотелось увидеть Tetris на дисплее, подключенном к ESP32. Используемый мной ESP-Wrover-Kit имеет цветной дисплей 320x240, может, стоит воспользоваться им? 15ИЭ, как и VT-50, с которого он был скопирован, может отображать 24 столбцов на 80 строк текста, то есть на отображение символа останется всего 4x10 пикселей Судя по ROM оригинального 15ИЭ, символы на нём занимали 8x8 пикселей, то есть нам понадобится вжать два горизонтальных пикселя в один К счастью, благодаря некоторым хитростям (в частности, LCD-пиксель, содержащий два горящих ужатых пикселя, я заставил быть ярче, чем LCD-пиксель только с одним ужатым пикселем) эффект оказался на самом деле довольно хорошо читаемым.

Терминал 15ИЭ с определёнными оговорками содержал в себе целый микропроцессор, построенный на дискретной логике. Хоть я и мог выполнить эмуляцию, чтобы приблизиться к реальности, я решил пойти простым путём и только смоделировать на C, как он интерпретирует управляющие символы последовательного потока данных. В конечном итоге получился далёкий от идеального симулятор 15ИЭ, но он достаточно хорош, чтобы отобразить игровое поле Tetris:


Очевидно, что простой демонстрации игрового поля недостаточно, но у платы ESP-Wrover-Kit недостаточно кнопок для управления падающими тетрамино. К счастью, у ESP32 есть Bluetooth и некто под ником Rossum написал эмулятор нескольких 8-битных консолей, способный использовать для ввода Bluetooth-геймпады и клавиатуры. Так как этот код выпущен под открытой лицензией, я смог взять его, избавиться от всего Arduino-подобного (к сожалению при этом потерялась поддержка Wiimote) и вставить его в мой код.

Теперь мне достаточно было найти Bluetooth-геймпад (в качестве которого я использовал один из джой-конов моего Switch) и играть в оригинальную версию Tetris на моей плате разработки ESP32:


Запуск BSD


Итак, теперь у меня есть первая версия Tetris, запущенная на симулируемом PDP-11. Однако это кажется пустой тратой ресурсов: для Tetris достаточно всего нескольких десятков килобайт ОЗУ, а у моего ESP32 есть и внутренняя ОЗУ, и 4 МиБ PSRAM. Почему бы немного не повысить ставки и не попробовать запустить ещё одну новаторскую программу, которая, вероятно, чуть больше подходит под возможности платы ESP32?

Ещё одна программа, впервые запущенная на PDP11, пришла из мира Unix. Хотя сам Unix не был рождён на PDP11 (строго говоря, он был рождён на позаимствованной PDP7, хоть и вскоре его портировали на PDP11), на этом компьютере появился дистрибутив Unix под названием BSD. Возможно, вы узнаете его как BSD Unix, который до сих пор существует в виде FreeBSD, NetBSD и OpenBSD. Кроме того, он стал фундаментом для более потребительских программ, например, Apple iOS и операционной системы линейки игровых консолей Playstation.

Ещё один аспект привлекательности BSD заключается в том, что её самая последняя версия (2.11BSD) содержит полный сетевой стек TCP/IP. Этот стек довольно известен: он частично использовался и в первых версиях Microsoft Windows. С тех пор IPv4 и TCP сильно не изменились, поэтому этот стек по-прежнему должен оставаться рабочим: у меня должно получиться взаимодействовать его с WiFi-интерфейсом ESP32, превратив его в истинно сетевой пример BSD, запущенной на PDP11.

Заставить BSD работать оказалось не так сложно. Сконфигурировать достаточно быструю машину, дать ей жёсткий диск на 1,5 ГиБ (физически расположенный на карте micro-SD, вставленной в разъём ESP-Wrover-Kit) и достаточно памяти вот и всё, что нужно для запуска BSD. Я выполнил установку ОС на жёсткий диск моего ноутбука, поскольку плёночный привод, необходимый для загрузки установочного носителя для BSD, был вырезан из версии SIMH для ESP32. Затем я переместил образ HD на карту micro-SD. Я смог снова использовать код терминала 15ИЭ, потому что он без проблем отображал и английские символы ASCII, и в результате смог увидеть загрузку BSD на своей плате разработки.

Однако я получил только несетевую версию BSD, потому что ранее вырезал из SIMH весь код сетевого интерфейса. (Даже если бы я его оставил его, ESP-IDF не поддерживает ни один предлагаемый способ связи, поэтому он всё равно был бы бесполезен.) К счастью, у ESP-IDF есть удобный и гибкий слой абстракции под названием ESP-NETIF, связывающий вместе сетевые интерфейсы и высокоуровневые вещи типа стеков TCP/IP. Я могу легко реализовать оболочку, получающую Ethernet-пакеты, приходящие от подсистемы WiFi, и перенаправляющую их в SIMH, и наоборот. SIMH уже содержит код для эмуляции Ethernet-карты, поддерживаемой 2.11BSD, поэтому всё должно замечательно работать.

Однако даже если PDP11 теперь способен передавать Ethernet-пакеты непосредственно драйверам WiFi для дальнейшей передачи, у меня всё равно нет хорошего решения для сетевого подключения. Во-первых, PDP11 не понимает таких связанных с WiFi аспектов, как SSID и тому подобное. Во-вторых, DHCP отсутствует в 2.11BSD, как и в его предшественнике BOOTP. Я даже задумался о портировании или реализации DHCP-клиента, однако в спецификациях DHCP есть одна странность требование прямого доступа к сетевым пакетом с обходом слоя TCP/IP. Насколько я знаю, ядро 2.11BSD не имеет поддержки ничего подобного. Хотя я и готов был к серьёзной работе, реализация нового интерфейса ядра на устаревшем десятки лет назад ядре это уже перебор.

Поэтому вместо этого я решил использовать непредусмотренным образом слой ESP-NETIF, чтобы система получила раздвоение личности. У ESP32 будет иметь полный стек TCP/IP с драйверами WiFi и DHCP-клиентом. У PDP11 также будет собственный полнофункциональный стек TCP/IP BSD. Оба стека будут иметь один IP-адрес, получаемый программным обеспечением ESP32 и передаваемый на PDP11. Однако небольшой кусок кода будет решать, какому стеку IP отправлять пакеты, полученные любым из интерфейсов. Это данные DHCP от интерфейса WiFi? Передай их на LwIP. Данные для telnet-сервера на интерфейсе WiFi? Это для PDP11. Код также может самостоятельно интерпретировать некоторые пакеты, приходящие от PDP11. Отправляя эти пакеты, PDP11 может выполнять поиск сетей WiFi, настраивать идентификационные данные WiFi и т.д. Так как PDP11 может передавать эти пакеты как стандартные широковещательные UDP-пакеты, нет необходимости в сложных интерфейсах конфигурирования на уровне ядра.


Эта конфигурация реализуется небольшой программой, запущенной на PDP11: её выполнение позволяет выполнять поиск доступных сетей и подключаться к одной из них. Далее программа автоматически получает данные сети от DHCP-клиента ESP32 LwIP и соответствующим образом конфигурирует сетевой интерфейс BSD. Программа называется wifid, как и говорилось выше, она использует широковещательные UDP-пакеты, подхватываемые написанной мной оболочкой ESP-NETIF. Код за пределами эмулятора подхватывает их и выполняет всю необходимую работу, при необходимости передавая обратно ответ широковещательными пакетами UDP.

Я не особо хотел писать подобный код на древнем компиляторе C, поставлявшемся с 2.11BSD, но, к счастью, это и необязательно: к моему большому удивлению, даже самый новый компилятор GCC замечательно поддерживает архитектуру PDP11. Скомпоновать его с двоичными файлами BSD было чуть сложнее, потребовался хак в виде добавления отсутствующей функции в библиотеки времени выполнения BSD. Кроме того, файлы заголовков, поставляемые 2.11BSD, используют формат, который сегодня справедливо признаётся неверным; поэтому вместо него я накидал ужасную, но работающую смесь заголовков RetroBSD, а также исправленных заголовков 2.11BSD. Конечный результат оказался некрасивым, но вполне рабочим: я могу скомпилировать двоичный файл на ноутбуке, при помощи FTP отправить его или в запущенный на ноутбуке SIMH, или в ESP32, а затем успешно запустить его там. Я считаю это свидетельством того, насколько любим был компьютер PDP11; несмотря на то, что это 50-летняя архитектура, коммерческая жизнь которой завершилась 30 лет назад, она по-прежнему имеет достаточно фанатов, которые отправляют патчи, официально включаемые в современный тулчейн GCC.

После завершения работы мне осталось только изменить один-два скрипта запуска сети для автоматического подключения PDP11 после перезапуска к последней использовавшейся сети. Теперь 2.11BSD имеет все возможности для подключения к любой сети WiFi.



Изготавливаем корпус


Тем временем мне наконец удалось получить документы, чтобы вернуться в Китай. После 14-дневного карантина мы вернулись домой и я получил доступ ко всему своему оборудованию. Настала пора избавиться от платы разработки и вставить эту сборку в красивый корпус!

Но для начала нужно понять, какой форм-фактор использовать. Сам PDP11 продавался во множестве форм-факторов, от огромных шкафов с гигантскими плёночными бобинами до машин PDP, больше всего похожих на PC. Отличаются от PC они тем, что не имеют стандартного интерфейса клавиатуры и монитора: для интерактивной работы необходимо подключать к машине некий внешний последовательный терминал.

DEC VT100 terminal

Поэтому я решил, что устройство, в основном существующее из-за своего внешнего вида и возможности интерактивности, стоит поместить в корпус, который пользователи видели чаще всего: в соответствующий возрасту машины последовательный терминал. Я выбрал DEC VT-100 (на самом деле, более популярный DEC VT-102), потому что он имеет очень красивый промышленный вид и легко узнаваем. Кроме того, это будет не первый случай установки всего PDP11 в этот терминал: линейка VT-100 проектировалась с учётом возможности расширения, и, например, VT-103 мог вмещать в себя небольшую систему PDP11. В случае разрабатываемого мной миниатюрного PDP11 терминал VT-100 обеспечит мне достаточно места для размещения логической платы и чего-нибудь ещё.

Перейдём к проектированию корпуса VT-102. Я взял несколько изображений из Интернета и использовал их для моделирования корпуса в OpenSCAD. Корпус не имеет никаких сложных изгибов, и это хорошо, потому что мне достаточно было для моделирования только фотографий сбоку и спереди. Так я получил модель VT-102 в более-менее реальных размерах, но для миниатюрной версии её, разумеется, нужно было уменьшить. Но насколько уменьшить? Ну, в основном это зависит от выбора дисплея, потому что в корпус должна помещаться передняя панель дисплея; все остальные детали находятся внутри корпуса, поэтому я могу перемещать их как угодно.

Я нашёл на Taobao очень милый 1,8-дюймовый LCD с разрешением 320x240, у которого оказался тот же контроллер, что и у платы разработки. (Также я нашёл 1,3-дюймовый дисплей с разрешением 240x240. Теоретически, благодаря субпиксельному сэмплированию я могу добиться половинного отображения текста, но в целом весь дисплей был слишком маленьким Металлическая штука сверху это разъём Micro-USB.) Поскольку основным ограничением был размер дисплея, теперь я мог уменьшить VT-102 под него. (Так как оригинальный VT-102 имел 12-дюймовый дисплей, моя модель становится копией примерно в масштабе 1/6,6. И это здорово, ведь таким же получился масштаб моей модели MacPlus.)

Так как теперь я знаю размер корпуса, то могу спроектировать электронную схему и спроектировать для неё печатную плату. Плата может располагаться сбоку корпуса, что позволит вывести разъёмы сзади от корпуса. Кроме того, это значит, что гибкую печатную плату LCD можно загнуть под углом 90 градусов и подключить к плате разъёмом. Компоненты печатной платы вполне обычные: мозги в виде модуля ESP32-Wrover, чип CH340 USB-to-serial для программирования флэш-памяти модуля без внешнего подключения программатора, литий-ионная зарядка и LDO 3,3 В. Также там есть разъём для карты micro-SD, которую можно использовать в качестве хранилища для жёсткого диска, а также гибкая печатная плата LCD.

Спустя какое-то время прибыли заказанные компоненты и изготовленная печатная плата. С пайкой разъёма гибкой печатной платы пришлось немного повозиться, но все другие компоненты установились довольно быстро.


Для фиксации LCD и печатной платы я спроектировал разъёмы, в которые должны вставляться углы платы и края LCD. Также я разрезал корпус на верхнюю и нижнюю половины, чтобы печатать их отдельно. У нижней и верхней половин есть соединительные кромки, обеспечивающие соединение без странных трещин. Я добавил два штифта для надёжного крепления половин. Мой принтер достаточно точен для печати приемлемой резьбы M3, поэтому я добавил её. Благодаря этому сборка выполняется очень легко: вставляем LCD, вставляем печатную плату, закрываем корпус, готово. Только если бы не выключатель питания и аккумулятор. Выключатель питания находится так далеко от всего остального (потому что там он выглядит лучше всего...), поэтому я закрепил его эпоксидкой. Что же касается аккумулятора, то я не знал, куда его вставить, поэтому подумал, что просто приклею его на двустороннюю ленту.


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


Базовый цвет печати скорее всего немного не совпал, потому что на большинстве фотографий PDP11 он более желтоватый, но этот фотополимер был ближе всего и сложно найти краску, лучше передающие цвет и текстуру. Однако от фирменной чёрной рамки вокруг ЭЛТ-дисплея я отказаться не мог. Вопрос решился малярным скотчем, аккуратной подрезкой и баллоном чёрной краски.



Итак, всё собрано и закрыто. На самом деле, спереди есть небольшой логотип Digital VT102, для создания которого потребовалось немного чёрной краски, но надпись в результате получилась почти читаемой. Клавиатура перед устройством это крошечная Bluetooth-клавиатура размером с кредитную карту, она немного велика относительно масштаба VT-100, но работает замечательно.


И на этом проект завершён.

Результат



В конечном итоге у меня получилась довольно красивая, созданная на 3D-принтере модель, на которой работает надёжный эмулятор PDP11 с ОС, к которому даже можно получать доступ через Интернет. Если вы хотите создать подобное устройство, то исходный код, проект печатной платы и файлы модели корпуса находятся на Github.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru