Продолжение серии статей о ShIoTiny — визуально программируемом контроллере на базе чипа ESP8266. Ключевой особенностью данного контроллера является возможность его программирования путем рисования программы в браузере. В данной статье описана краткая теория аналого-цифрового преобразования и практическое применение АЦП контроллера ShIoTiny. Предыдущие статьи серии. ShIoTiny: малая автоматизация, интернет вещей или «за полгода до отпуска» ShIoTiny: узлы, связи и события или особенности рисования программ ShIoTiny: вентиляция влажного помещения (проект-пример) ShIoTiny и окружающий мир: подключение датчиков к бинарным входам, дребезг контактов и другие вопросы Сайт проекта Бинарные прошивки, схема контроллера и документация тут В предыдущей статье мы рассмотрели подключение основных типов датчиков с бинарным выходом к бинарным входам контроллера ShIoTiny. Но, как известно большей части людей, а также школьников и студентов, основная часть информации об окружающем мире — это аналоговые величины различной физической природы: сила света и звука, скорость движения, давление воздуха, уровень жидкости и так далее. Практически все современные микропроцессоры и микроконтроллеры способны обрабатывать только дискретные значения в двоичном представлении.  Для тех, кто еще не знает чем отличается аналоговая величина от дискретной напишу краткие пояснения. Кто уже все и так знает — может их пропустить. Сразу оговорюсь — аналогии и упрощения имеют место быть. Это не диссертация, а скорее краткие объяснения на пальцах. Математику в школе изучали все. Поэтому обратимся к ней и проведем аналогии между аналоговыми и дискретными величинами и числами. С точки зрения математики — аналоговая величина есть число вещественное, определенное в любой точке заданного отрезка числовой прямой. Дискретная величина с точки зрения математики есть число целое. И определено оно только в определенных точках заданного отрезка числовой прямой. На рисунке ниже схематично представлено расположение аналоговых и дискретных величин-чисел на числовой прямой.  Для примера рассмотрим отрезок числовой прямой от -4 до 3. Как видим, дискретных величин, обозначенных красными точками — целых чисел на нем всего 8 штук. Аналоговых величин, показанных зеленой линией на рисунке — бесконечное множество. Например, у нас есть некая величина X, имеющая диапазон значений от 0 до 127. Если мы представляем эту величину как аналоговую, то теоретически мы можем представить ее с любой точностью — например 12.123455454980 или 126.00000000007 или вообще с миллионом знаков после запятой. Но как только в дело вступает микроконтроллер и величина X обретает дискретное представление — то ни о какой «бесконечной точности» и речи идти не может даже теоретически. Точность ограничена числом двоичных разрядов, которое мы отводим под представление величины Х. Например, мы возьмем 7 двоичных разрядов. В этом случае мы можем представить величину Х с точностью единица. То есть можно будет указать X=1 или X=112. А вот Х=112.5 уже указать не получится — разрядности не хватит. Если мы возьмем для представления той же величины Х не 7, а 10 разрядов, то точность представления будет уже не единица, а 0.125. И в таком виде можно представить Х=95.125 или X=112.5. Но точнее, например в виде X=112.13 — уже представить эту величину нельзя. Если вас смущает то, что я пишу дробные значения и при этом говорю о них как о целых числах то вспомните, что «дискретные точки» можно расставить на числовой прямой не через единицу, а например через 0.5 или 0.125 единицы. Но их как было, так и останется конечное значение на любом отрезке. И все свойства дискретной величины сохранятся. Ключевое отличие дискретной величины от аналоговой — это то, что на любом конечном отрезке числовой прямой будет конечное число дискретных (целых) значений и бесконечное число значений аналоговых (вещественных). Соответственно получаем, что дискретное представление величин всегда обладает конечной точностью. В итоге всего выше сказанного получаем тривиальный вывод. Большая часть измеримых величин реального мира — аналоговые. Микроконтроллеры работают только с дискретными числовыми представлениями величин. Поэтому, прежде, чем обрабатывать какую-нибудь аналоговую величину с помощью микроконтроллера — эту величину необходимо представить в виде дискретной. Да еще и в двоичном виде. Такое преобразование из аналогового представления в дискретное называется аналого-цифровым преобразованием. Устройство для преобразования аналогового сигнала в цифровой называется АЦП (аналого-цифровой преобразователь). Обычно такое устройство имеет один или несколько аналоговых входов, на которые подается аналоговый сигнал и цифровой выход с заданной разрядностью (обычно — от 8 до 16 разрядов). В современных микроконтроллерах, в том числе и в нашем ESP8266, имеются встроенные блоки АЦП. Какие характеристики имеют АЦП вообще и АЦП ESP8266 в частности? Первая характеристика — это какую именно входную аналоговую величину АЦП преобразует в выходной цифровой код. Чаще всего эта величина — напряжение на аналоговом входе АЦП. Так будет и в нашем случае. Но в природе существуют и АЦП с токовым входом. Вторая характеристика АЦП, которая нужна на практике — это диапазон значений входной величины АЦП. В нашем случае — это минимальное и максимальное значения напряжения на входе АЦП. Эти значения будут 0В и 1В соответственно. Диапазон входных значений 1В кажется небольшим, но большие напряжения всегда можно поделить и уменьшить, а малые напряжения — усилить. Третья и, пожалуй, самая важная характеристика АЦП это его разрядность. Эта величина определяет точность преобразования или (по нашей аналогии) — как часто расставлены "дискретные" точки на "вещественной" числовой прямой. В нашем случае АЦП имеет разрядность 10. Что это значит? А это значит, что выходной цифровой код представлен 10 двоичными разрядами и имеет 1024 значения — от 0 до 1023.
Строго говоря, стоит помнить, что точность преобразования зависит не только от разрядности, но и от ряда других параметров, например линейности АЦП. Но об этом уже много написано очень умными дядями в очень умных книгах, поэтому в этой статье я оставлю читателя без подробностей. Кроме того АЦП ESP8266 умеет определять переполнение, то есть ситуацию, когда на вход подано напряжение больше 1В. Если вы возьмете умный справочник по АЦП и загляните туда — то там будут еще десятки характеристик. Все они нужные и важные, но так далеко мы углубляться не будем. Например, мы не будем касаться в временных параметров АЦП, так как считаем, что в нашем случае измеряемые величины изменяются достаточно медленно и АЦП их преобразует в цифровое представление «мгновенно». Подведем предварительный итог.
В контроллере ShIoTiny имеется АЦП, встроенный в ESP8266. На вход АЦП ESP8266 подается напряжение в диапазоне от 0 до 1В. На выходе АЦП ESP8266 мы получаем число, пропорциональное входному напряжению в диапазоне от 0 до 1023. Напряжению 0В соответствует код 0 на выходе АЦП, напряжению 1В соответствует код 1023 на выходе АЦП. Считывание данных с АЦП в ShIoTiny производятся со скоростью примерно 10 раз в секунду. Аппаратно вход АЦП защищен от перенапряжения, аналогично тому, как защищены бинарные входы Input1,2,3 (см. тут). Вот и все, что касается аппаратного обеспечения АЦП ShIoTiny. Займемся теперь узлом ADC1, который обрабатывает данные с аппаратного АЦП ESP8266. На схеме-программе в редакторе ElDraw, узел аналого-цифрового преобразователя называется ADC1. Как уже упоминалось, узел ADC1 примерно 10 раз в секунду получает данные с аппаратного АЦП ESP8266. Но на этом указанный узел не успокаивается, а начинает эти данные обрабатывать и даже чуть-чуть анализировать. Во-первых, проверяется — а не было ли переполнения АЦП? То есть — а не подали ли на вход АЦП больше 1В? Если такая ситуация обнаружена — то на выход узла ADC1 выставляется значение NAN (не число). Во-вторых, если не было переполнения, то выходное значение АЦП 0..1023 преобразуется в величину напряжения на входе АЦП  — число с плавающей точкой в диапазоне 0..1. В-третьих, это преобразованное значение 0..1 пересчитывается по формуле , где  — напряжение на входе АЦП (от 0 до 1В); k — диапазон (ADC range) и b — смещение (ADC offset). И, наконец, полученное значение X выставляется на выход узла ADC1. И, наконец в пятых. Если значение величины Х изменилось на заданный процент (от 1 до 100%), то узел АЦП генерирует события, вызывая пересчет значений подключенных к нему узлов. Это по сути параметр «чувствительности АЦП» (ADC changes, range %). Ведь обычно нет смысла реагировать «на каждый чих», то есть на мизерные изменения в младших разрядах АЦП — они часто «шумят». Поэтому параметр чувствительности имеет большое практическое значение. Встает законный вопрос — как нам настроить эти параметры? Ткните указателем мыши в узел ADC1 на схеме и тут же у вас появится окно настройки. В нем можете выставить все, что вам нужно. Для нашего случая это будет такое окно, как на рисунке.  В этом окне можно задать все параметры, о которых говорилось выше — диапазон, смещение и чувствительность АЦП. Если вы ничего не задали, то диапазон будет равен 1. Смещение — ноль. И чувствительность — 1%. То есть по умолчанию, фактически на выходе узла ADC1 будет значение аналогового напряжения, подаваемого на вход АЦП. Как видим, узел ADC1 довольно сложен. Зачем все это сделано? Да для вас, дорогие мои пользователи! Шучу, конечно, как злобный эгоист я учел предыдущий опыт и постарался облегчить себе жизнь. Нам, как простым инженерам, хочется чтобы величины были представлены не в «попугаях», а в нормальных и понятных значениях — вольтах, амперах, килограммах или метрах. Многие датчики выдают значение именно «в попугаях», надеясь на то, что умный микроконтроллер пересчитает их в нужные величины. Именно для этого и был введен пересчет измеренного АЦП значения по функции заданной. Но, как водится, лучше один раз увидеть, чем десять раз услышать. Так же как лучше один раз попробовать, чем десять раз увидеть… Но не о том речь.
Потому приведу пару не сложных примеров: систему контроля питающей сети и систему измерения температуры на основе датчика с токовым выходом 4-20мА. Измерение напряжения — часто возникающая задача. Например, мы хотим измерить напряжение питающей сети ~220В. Если у нас плохо с электросетью — задача очень реальная. Нам не нужно очень-очень точного изменения. Достаточно чтобы при превышении напряжения на 15% от нормы на ShIoTiny срабатывало реле Relay1, а при понижении напряжения на 15% от нормы срабатывало реле Relay2. Разумеется, воткнуть вход ADC1 контролера ShIoTIny в розетку мы не можем. Что делать? Во-первых напряжение надо понизить до приемлемого уровня — 0..1В. И, во-вторых его надо выпрямить: переменное напряжение наш АЦП измерить не может. Напряжение в сети может быть как ниже нормы, так и выше нормы. Для простоты положим, что 220В сетевого напряжения будет соответствовать 0.5В напряжения на входе АЦП. Далее — ищем любой понижающий трансформатор, который при ~220В входного напряжения выдаст нам, скажем ~3В выходного напряжения и собираем такую схему, как на рисунке ниже. Тут несведущего в электронике может поджидать сюрприз. На выходе выпрямителя неожиданно появляется не 3В постоянного напряжения, а какие-то 4В с лишним! На самом деле все просто объясняется. Когда мы измеряем переменное напряжение, то вольтметр показывает нам действующее значение напряжения. А когда мы это напряжение выпрямляем — то на выходе выпрямителя получаем пиковое значение напряжения, которое для синусоидального сигнала примерно в 1.41, а точно в  раз больше действующего. Отсюда и "непонятные" 4.23В на выходе выпрямителя. Ну и наконец, нам надо рассчитать делитель напряжения, то есть сопротивления R1 и R2. Нам необходимо получить на выходе делителя 0.5В при напряжении на его входе 4.23В. Значит выпрямленное напряжение 4.23В надо поделить в 8.46 раз. Для этого надо установить резистор R2=100 Ом, а резистор R1=746 Ом. Но это в идеале. Реально резисторов сопротивлением 746 Ом не бывает. Да и трансформаторы особой точностью не отличаются. Поэтому, если кто рискнет попробовать это решение, я настоятельно советую поставить резистор R1=760 Ом, а резистор R2 взять подстроечный, сопротивлением 180 Ом или на 220 Ом. Тогда вы сможете, вооружившись вольтметром, отрегулировать R2 так, что при ~220В напряжения на первичной обмотке трансформатор, на выходе делителя (или, что то же самое, на входе ADC1) будет =0.5В. Мы измеряем напряжение не просто так, а чтобы по его превышению или понижению от нормы что-то делать. Например, включать резервное питание, чтобы какое-то устройство не сгорело и не отключилось. Поэтому нарисуем простейшую схему-программу, которая при превышении напряжения на 15% от нормы заставляет срабатывать реле Relay1, а при понижении напряжения на 15% от нормы заставляет срабатывать реле Relay2. Кроме того, повышенное или пониженное напряжение должны продержаться в сети не менее 1 минуты, чтобы вызвать срабатывание реле. Этим мы избежим ложных срабатываний при коротких пиках напряжения, которые часто возникают в сети. Схема-программа, реализующая нашу задумку, показана на рисунке.  Чтобы эта схема работала, надо в настройках параметров АЦП выставить коэффициент k (диапазон) равный 440, как показано на рисунке.  При коэффициенте 440 и напряжении напряжении на входе АЦП 0.5В на выходе узла ADC1 будет значение 220. То есть реальное сетевое напряжение! Это очень удобно, потому что позволяет задать константы сразу в вольтах: 220В+15% это 253В и 220В-15% это 187В. При необходимости эти значения можно легко изменить, не тратя время на расчет и перевод напряжения в «попугаи». Датчики, имеющие токовый выход 4-20мА очень распространены в промышленности. В быту их встретишь не часто. Но тем не менее у кого-то они есть и этот кто-то хочет приспособить их к делу. АЦП позволяет использовать такие датчики совместно с контролером ShIoTiny. Почему выход датчика токовый и именно 4-20мА? Поясню. Токовые выходы хорошо работают на длинных линиях. Скажем километр. Им плевать на сопротивление проводов: ток одинаков по всей длине провода вне зависимости от сопротивления проводников. Начальное значение тока 4мА, а не просто отсутствие тока позволяет легко обнаружить обрыв провода. Если датчик целый и провод не оборван — то ток всегда есть. Хотя бы 4мА. А если оборвался провод — тока нет (0мА). Предположим, у нас есть датчик температуры с токовым выходом 4-20мА и диапазоном измеряемой температуры от -40С  до +125С. Мы хотим его подключить к ShIoTinу. Первое, что нам нужно сделать — это преобразовать ток в напряжение. Идеальным средством для такого преобразования является обычный резистор. Так как максимальное напряжение на входе АЦП 1В, а максимальный ток в линии — 20мА, то несложно посчитать, что резистор, преобразующий 20мА в 1В будет иметь сопротивление 50 Ом. (Не знаешь закон Ома — сиди дома!). Мы подключим наш датчик так, как показано на рисунке.  Датчик является генератором тока, пропорционального измеренной температуре. При сопротивлении 50 Ом, подключенном параллельно входу ADC1, на входе АЦП будут следующие значения напряжения, в зависимости от тока, создаваемого в цепи датчика: Предположим, мы хотим измерить температуру и опубликовать ее по MQTT. Кроме того мы будем публиковать состояние датчика (обрыв линии, КЗ или все в порядке). Первое, что нам надо сделать — пересчитать значение из «попугаев» в градусы. Зная, что температуре -40С соответствует ток 4мА и напряжение на входе АЦП 0.2В, а температуре +125С соответствует ток 20мА и напряжение на входе АЦП 1В, получаем коэффициенты: k=206.25 и b=-81.25. Вводим эти коэффициенты в окне настройки АЦП, как показано на рисунке.  Кто хочет сам проверить правильность расчета k и b — решите сами простейшую систему систему уравнений:   Ну а схема-программа будет совсем не сложной и приведена на рисунке ниже.  В случае, когда все хорошо и датчик температуры 4-20мА исправен — температура публикуется на MQTT сервере под именем /t_sens. Признаки аварии так же публикуются под именами /sens_short и /sens_break. Если все хорошо, то признаки аварии — нулевые. Если произошел обрыв линии, то значение температуры будет меньше -40С. В этом случае параметр /sens_break на MQTT брокере будет опубликован как единица. Если произошло КЗ в линии, то значение температуры будет больше +125С. В этом случае у нас на входе АЦП будет напряжение больше 1В и узел АЦП выставит на своем выходе значение NAN (не-число). В этом случае параметр /sens_short на MQTT брокере будет опубликован как единица. При возникновении любой из аварий сработает реле Relay3, которое может, например, отключить питание датчика. Получился вполне себе «умный» датчик, который можно вынести, например, за 200 метров от контроллера и измерять температуру. Закончив обзор примеров АЦП на ShIoTiny я понял, что его можно продолжать и продолжать. Но это будет уже на статья, а учебник. Прошу снисхождения, если кому-то материал показался излишне «детским» и подробным, а кому-то напротив, что-то осталось не ясно. Как обычно — конструктивная критика и предложения приветствуются. Кроме этого приветствуются замечания и предложения. Отправлять все это можно как обычно в комментарии или на почту: shiotiny@yandex.ru. Так как в статье есть масса стороннего материал, который требует пояснения, то без ссылок не обойтись. Как происходит преобразование аналогового сигнала в цифровой
 Аналого-цифровой преобразователь Аналоговый и цифровой сигнал. Типы сигналов и как это действует Список параметров напряжения и силы электрического тока
 Аналоговые полевые интерфейсы: токовая петля 4-20 мА – от простого к сложному НАСТРОЙКА ОБЛАЧНОГО УПРАВЛЕНИЯ ESPPOWER ЧЕРЕЗ ИНТЕРНЕТ С ПОМОЩЬЮ ANDROID