Оглавление
Простой таймер Ардуино millis()
Чтобы понять принцип работы функции millis() Arduino продемонстрируем пример программы счетчика с выводом времени на монитор порта. Команда millis позволяет осуществлять задержу в выполнении программы без delay и осуществлять при выполнении программы многозадачность. Отсчет времени начинается сразу после загрузки программы в микроконтроллер и открытия монитора порта Arduino IDE.
// переменные для отсчета минут и секунд int SEC = 0; int MIN = 0; unsigned long timer; void setup() { Serial.begin(9600); timer = millis(); } void loop() { // если прошло 995 мс — прибавляем одну секунду к переменной SEC // если прошло 60 секунд, прибавляем одну минуту if (millis() — timer > 995) { timer = millis(); SEC = SEC + 1; if (SEC > 60) { SEC = 1; MIN = MIN + 1; } // выводим текущий счетчик времени на монитор порта Serial.println(String(MIN) + » : » + String(SEC)); } }
Пояснения к коду:
- секунда прибавляется через 995 мс, так как на выполнение других операций тратится некоторое количество миллисекунд. Если программируемый таймер будет спешить, то количество миллисекунд следует добавить.
Мигаем светодиодом без delay()
или всегда ли официальный примеру учат «хорошему».
Обычно это одна из первых проблем с которой сталкивается навичок в микроконтроллерх. Помигал диодом, запустил стандартный скетч blink(), но как только он него возникает желание что-бы контроллер «занимался чем-то еще» его ждет неприятный сюрприз — тут нет многопоточности. Как только он написали где-то что-то типа delay(1000) — обнаруживается что на это строчке «контроллер остановился» и ничего больше не делает (кнопки не опрашивает, датчики не читает, вторым диодом «помигать» не может).
Новичок лезет с этим вопросом на форум и тут же получает ушат поучений: «отказывайтесь от delay()», учитесь работать с millis() и в прочитайте, в конце концов базовые примеры. В частности Мигаем светодиодом без delay()
Приведу его код:
/* Blink without Delay 2005 by David A. Mellis modified 8 Feb 2010 by Paul Stoffregen */ const int ledPin = 13; // номер выхода, подключенного к светодиоду // Variables will change: int ledState = LOW; // этой переменной устанавливаем состояние светодиода long previousMillis = 0; // храним время последнего переключения светодиода long interval = 1000; // интервал между включение/выключением светодиода (1 секунда) void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(ledPin, OUTPUT); } void loop() { // здесь будет код, который будет работать постоянно // и который не должен останавливаться на время между переключениями свето unsigned long currentMillis = millis(); //проверяем не прошел ли нужный интервал, если прошел то if(currentMillis - previousMillis > interval) { // сохраняем время последнего переключения previousMillis = currentMillis; // если светодиод не горит, то зажигаем, и наоборот if (ledState == LOW) ledState = HIGH; else ledState = LOW; // устанавливаем состояния выхода, чтобы включить или выключить светодиод digitalWrite(ledPin, ledState); } }
В принципе отправка к этому примеру — вполне правильна. В нем действительно виден стандартный паттерн как нужно выполнять какое-то переодическое действие (или отложенное):
1. Сохраняем время в какую-то переменную
2. В loop() все время смотрим на разницу «текущие-время — сохраненное»
3. Когда эта разница превысила какое-то значение (нужный нам «интервал переодичности»)
4. Выполняем какое-то действие (меняем состояние диода, заново запоминаем «стартовое время и т.п.»)
С задачей «объяснить идею» — пример справляется. Но, с моей точки зрения, в нем есть несколько методологических ошибок. Написание скетчек в подобно стиле — рано или поздно приведет к проблемам.
Итак, что же тут не так?
1. Не экономно выбираем тип переменных
Переменная ledPin у нас объявлена как тип int. Зачем? Разве номер пина может быть очень большим числом? Или может быть отрицательным числом? Зачем под его хранение выделять два байта, когда вполне хватит одного. Тип byte может хранить числа от 0 до 255. Для номера пина — этого вполне хватит.
const byte ledPIN = 13; // номер выхода, подключенного к светодиоду
Этого будет вполне достаточно.
2. А зачем нам переменная для малых чисел?
А зачем нам тут вообще переменая? (пусть и объявленная как const). Зачем тратить такты процессора на чтение переменной? И расходовать еще один байт? Воспользуемся директивой препроцессора #define
#define LED_PIN 13 // номер выхода, подключенного к светодиоду
Тогда еще на этапе компиляции компилятор просто подставить 13-ть везде где в коде используется LED_PIN и не будет выделять отдельных переменных.
3. Тип int опять выбрали как «первое что в голову пришло»?
И опять спотыкаемся на объявлении следующей же переменной. Почему ledState опять int? Кроме того что снова «два байта там где можно один использовать», так еще и «смысловая нагрузка» теряется. Что у нас хранится в переменной? Состояние светодиода. Включен/выключен. Горит/Не горит. Явно же напрашивается тип boolean. По крайней мере до тех пор, пока светодиод у нас может принимать два состояния.
Подключение поворотного энкодера с Ардуино
Теперь, когда принципы работы различных энкодеров изучены, можно приступить к описанию схемы подключения к Ардуино.
Для этого понадобятся:
- любое устройство Ардуино, например, Arduino UNO, Arduino Mega, Arduino Leonardo, Arduino 101, Arduino Due;
- любой энкодер Ардуино.
Обзор поворотного энкодера
Поворотный энкодер — это датчик, используемый для определения углового положения вала, подобный потенциометру.
Пины, и что они означают:
- CLK: выход A (цифровой);
- DT: выход B (цифровой);
- SW: нажатие кнопки (цифровой);
- + : VCC-напряжение питания;
- GND: заземление.
Поворотный прибор может быть использован в основном для тех же целей, что и потенциометр. Однако потенциометр обычно имеет точку, за которую вал не может вращаться, в то время как энкодер может вращаться в одном направлении без ограничений. Чтобы сбросить показания положения, нужно нажать на вал вниз.
Данное устройство определяет угловое положение вращающегося вала с помощью серии прямоугольных импульсов. Он по существу имеет равномерно расположенные контактные зоны, соединенные с общим узлом, а также два дополнительных контакта, называемых A и B, которые находятся на 90 градусов вне фазы. Когда вал вращается вручную, контакты A и B синхронизируются с общим контактом и генерируют импульс. Подсчитав количество импульсов любого из этих выходов, можно определить положение вращения.
Чтобы определить направление и проверить, вращается ли штифт по часовой стрелке или против часовой стрелки, нужно сделать следующее:
- Если вращающийся вал движется по часовой стрелке, то сигнал A опережает B. В одни и те же моменты времени, A и B будут находиться на противоположных частях прямоугольной волновой функции.
- Если вал движется против часовой стрелки, то сигнал B опережает A.
Подключение
Если говорить в общем, то CLK, DT и SW, должны быть подключены к цифровым выводам на Ардуино, + должен быть подключен к 5V, а GND заземлен.
Пошаговая инструкция подключения проводов энкодера к Ардуино:
- CLK: подключите конец провода к пину CLK на поворотном энкодере, затем к любому цифровому выводу на Arduino (оранжевый провод).
- DT: подключите конец провода к пину DT, затем к любому цифровому контакту на Arduino (желтый провод).
- SW: подключите конец провода к пину SW, далее к любому цифровому контакту на Arduino (голубой провод).
- + : подключите провод к пину +, затем к контакту +5V на Arduino (красный провод).
- GND: подключите конец провода к пину GND на энкодер с контактом GND на Arduino. (Черный провод).
Как кодировать
Код изменяет высоту тона в зависимости от того, в каком направлении повернут энкодер. Когда он поворачивается против часовой стрелки, шаг уменьшается, а когда он поворачивается по часовой стрелке, шаг увеличивается.
Что понадобится:
- датчик поворотного энкодера;
- Ардуино;
- пьезодатчик;
- провода.
Вот сам код:
Описание кода
Итак, сначала нужно определить контакты, к которым подключен кодер, и назначить некоторые переменные, необходимые для работы программы. В разделе «Настройки» нужно определить два контакта в качестве входных данных, и запустить последовательную связь для печати результатов на последовательном мониторе. Также нужно прочитать начальное значение вывода A, затем поместить это значение в переменную aLastState.
Далее в разделе цикла снова изменить вывод A, но теперь поместить значение в переменную aState. Таким образом, если повернуть вал и сгенерировать импульс, эти два значения будут отличаться. Сразу после этого, используя второй параметр «if», определить направление вращения. Если выходное состояние B отличается от A, счетчик будет увеличен на единицу, в противном случае он будет уменьшен. В конце, после вывода результатов на мониторе, нужно обновить переменную aLastState с помощью переменной aState.
Это все, что нужно для этого примера. Если загрузить код, запустить монитор и начать вращать вал, значения станут отображаться на мониторе.
Упрощенный пример
Следующий пример кода продемонстрирует, как считывает сигналы Arduino на датчике энкодера. Он просто обновляет счетчик (encoder0Pos) каждый раз, когда энкодер поворачивается на один шаг, а параметры вращения отправляются на порт ПК.
Код:
Следует обратить внимание на то, что приведенный выше код не является высокопроизводительным. Он предоставлен для демонстрационных целей
Элементы платы
Дисплей
Дисплей MT-16S2H-I умеет отображать все строчные и прописные буквы латиницы и кириллицы, а также типографские символы. Для любителей экзотики есть возможность создавать собственные иконки.
Экран выполнен на жидкокристаллической матрице, которая отображает 2 строки по 16 символов. Каждый символ состоит из отдельного знакоместа 5×8 пикселей.
Контроллер дисплея
Матрица индикатора подключена к встроенному чипу КБ1013ВГ6 с драйвером расширителя портов, которые выполняют роль посредника между экраном и микроконтроллером.
Контроллер КБ1013ВГ6 аналогичен популярным чипам зарубежных производителей HD44780 и KS0066, что означает совместимость со всеми программными библиотеками.
I²C-расширитель
Для экономии пинов микроконтроллера на плате дисплея также распаян дополнительный преобразователь интерфейсов INF8574A: микросхема позволит общаться экрану и управляющей плате по двум проводам через интерфейс I²C.
Контакты подключения
На плате дисплея выведено 18 контактов для подведения питания и взаимодействия с управляющей электроникой.
Вывод | Обозначение | Описание |
---|---|---|
1 | GND | Общий вывод (земля) |
2 | VCC | Напряжение питания (5 В) |
3 | VO | Управление контрастностью |
4 | RS | Выбор регистра |
5 | R/W | Выбор режима записи или чтения |
6 | E | Разрешение обращений к индикатору (а также строб данных) |
7 | DB0 | Шина данных (8-ми битный режим)(младший бит в 8-ми битном режиме) |
8 | DB1 | Шина данных (8-ми битный режим) |
9 | DB2 | Шина данных (8-ми битный режим) |
10 | DB3 | Шина данных (8-ми битный режим) |
11 | DB4 | Шина данных (8-ми и 4-х битные режимы)(младший бит в 4-х битном режиме) |
12 | DB5 | Шина данных (8-ми и 4-х битные режимы) |
13 | DB6 | Шина данных (8-ми и 4-х битные режимы) |
14 | DB7 | Шина данных (8-ми и 4-х битные режимы) |
15 | LED+ | Питания подсветки (+) |
16 | LED– | Питания подсветки (–) |
17 | SDA | Последовательная шина данных |
18 | SCL | Последовательная линия тактированния |
Обратите внимания, что физические контакты подсветки экрана и , также интерфейс шины I²C и расположены не в порядком соотношении с другими пинами экрана.
Питание
Экран совместим со всеми контроллерами с логическим напряжением от 3,3 до 5 вольт. Но для питания самого индикатора (пин VCC) необходимо строго 5 вольт
Если в вашем проекте нет линии 5 вольт, обратите внимание на дисплей текстовый экран 16×2 / I²C / 3,3 В.
Интерфейс передачи данных
Дисплей может работать в трёх режимах:
- 8-битный режим — в нём используются и младшие и старшие биты (-)
- 4-битный режим — в нём используются только младшие биты (-)
- I²C режим — данные передаются по протоколу I²C/TWI. Адрес дисплея .
Использовать восьмибитный и четырёхбитный режим в данном дисплее не целесообразно. Ведь главное достоинство этой модели именно возможность подключения через I²C.
Если всё-таки есть необходимость использовать 4-битный или 8-битный режим, читайте документацию на текстовый экран 16×2.
Объединение питания
Для подключения питания к дисплею необходимо пять контактов:
Вывод | Обозначение | Описание |
---|---|---|
1 | GND | Общий вывод (земля) |
2 | VCC | Напряжение питания (5 В) |
3 | VO | Управление контрастностью |
15 | LED+ | Питания подсветки (+) |
16 | LED– | Питания подсветки (–) |
Но если запаять перемычки и на обратной стороне дисплея, количество контактов питания можно сократить до трёх, объединив цепь питания и подсветки дисплея.
Мы взяли этот шаг на себя и спаяли перемычки самостоятельно.
Выбор адреса
Используя шину можно подключить несколько дисплеев одновременно, при этом количество занятых пинов останется прежним.
Для общения с каждым дисплеем отдельно, необходимо установить в них разные адреса. Для смены адреса на обратной стороне дисплея установлены контактные площадки , и .
Капнув припоем на контактные площадки, мы получим один из семи дополнительных адресов:
- нет припоя, соответственно нет электрического контакта.
- есть припой, соответственно есть электрический контакт.
J2 | J1 | J0 | Адрес |
---|---|---|---|
L | L | L | 0x38 |
L | L | H | 0x39 |
L | H | L | 0x3A |
L | H | H | 0x3B |
H | L | L | 0x3C |
H | L | H | 0x3D |
H | H | L | 0x3E |
H | H | H | 0x3F |
Выбор таймера
На всех платформах доступно значение TIMER_DEFAULT, которое указывает на рабочий таймер по умолчанию.
Если есть необходимость использовать другой таймер, можно указать его имя напрямую: _TIMER1…TIMER9, _TIMER1_32BIT…_TIMER9_32BIT
Однако для разных аппаратных платформ доступно разное количество таймеров с разными именами, поэтому указание конкретного таймера по имени может привести к потере кросс-платформенности.
Доступные таймеры:
-
AVR ATmega1280, ATmega2560: _TIMER1, _TIMER3,_TIMER4, _TIMER5; TIMER_DEFAULT = _TIMER5
-
AVR AT90USB646, AT90USB1286, ATmega128, ATmega1281, ATmega1284, ATmega1284P, AVR_ATmega2561: _TIMER1, _TIMER3; TIMER_DEFAULT = _TIMER3
-
AVR ATmega32U4: _TIMER1; TIMER_DEFAULT = _TIMER1
-
AVR другие чипы: _TIMER1; TIMER_DEFAULT = _TIMER1
-
PIC32 (ChipKIT): _TIMER1, _TIMER2, _TIMER3, _TIMER4, _TIMER5, _TIMER2_32BIT, _TIMER4_32BIT; TIMER_DEFAULT = _TIMER4_32BIT
-
SAM (Arduino Due): _TIMER1/_TIMER1_32BIT, _TIMER2/_TIMER2_32BIT, _TIMER3/_TIMER3_32BIT, _TIMER4/_TIMER4_32BIT, _TIMER5/_TIMER5_32BIT, _TIMER6/_TIMER6_32BIT, _TIMER7/_TIMER7_32BIT, _TIMER8/_TIMER8_32BIT, _TIMER9/_TIMER9_32BIT (все таймеры 32-битные, _TIMERX_32BIT == _TIMERX); TIMER_DEFAULT = _TIMER3/_TIMER3_32BIT;
Встроенные варианты частот
/** * freq: 500KHz = 500000 ops/sec * period: 1sec/500000 = 2us */ void timer_init_ISR_500KHz(int timer); /** * freq: 200KHz = 200000 ops/sec * period: 1sec/200000 = 5us */ void timer_init_ISR_200KHz(int timer);s /** * freq: 100KHz = 100000 ops/sec * period: 1sec/100000 = 10us */ void timer_init_ISR_100KHz(int timer); /** * freq: 50KHz = 50000 ops/sec * period: 1sec/50000 = 20us */ void timer_init_ISR_50KHz(int timer); /** * freq: 20KHz = 20000 ops/sec * period: 1sec/20000 = 50us */ void timer_init_ISR_20KHz(int timer); /** * freq: 10KHz = 10000 ops/sec * period: 1sec/10000 = 100us */ void timer_init_ISR_10KHz(int timer); /** * freq: 5KHz = 5000 ops/sec * period: 1sec/5000 = 200us */ void timer_init_ISR_5KHz(int timer); /** * freq: 2KHz = 2000 ops/sec * period: 1sec/2000 = 500us */ void timer_init_ISR_2KHz(int timer); /** * freq: 1KHz = 1000 ops/sec * period: 1sec/1000 = 1ms */ void timer_init_ISR_1KHz(int timer); /** * freq: 500Hz = 500 ops/sec * period: 1sec/500 = 2ms */ void timer_init_ISR_500Hz(int timer); /** * freq: 200Hz = 200 ops/sec * period: 1sec/200 = 5ms */ void timer_init_ISR_200Hz(int timer); /** * freq: 100Hz = 100 ops/sec * period: 1sec/100 = 10ms */ void timer_init_ISR_100Hz(int timer); /** * freq: 50Hz = 50 ops/sec * period: 1sec/50 = 20ms */ void timer_init_ISR_50Hz(int timer); /** * freq: 20Hz = 20 ops/sec * period: 1sec/20 = 50ms */ void timer_init_ISR_20Hz(int timer); /** * freq: 10Hz = 10 ops/sec * period: 1sec/10 = 100ms */ void timer_init_ISR_10Hz(int timer); /** * freq: 5Hz = 5 ops/sec * period: 1sec/5 = 200ms */ void timer_init_ISR_5Hz(int timer); /** * freq: 2Hz = 2 ops/sec * period: 1sec/2 = 500ms */ void timer_init_ISR_2Hz(int timer); /** * freq: 1Hz = 1 ops/sec * period: 1sec */ void timer_init_ISR_1Hz(int timer);
Работа проекта
Принцип работы проекта достаточно прост. После запуска программы на экране ЖК дисплея высветится надпись “Arduino Timer” и она будет гореть до тех пор пока вы не нажмете кнопку. После нажатия кнопки программа попросит вас ввести время, с которого начнется обратный отсчет, при помощи вызова функции “setFeedingTime”. Время вводится с помощью клавиатуры. После ввода времени необходимо нажать клавишу ‘D’ на клавиатуре чтобы сохранить введенное время и начать обратный отсчет.
Далее в функции loop() мы будем выполнять ряд вычислений чтобы осуществить уменьшение счета (секунда за секундой) и показывать на ЖК дисплее оставшееся до истечения таймера время в формате HH:MM:SS. Более подробно все эти процессы показаны на видео, приведенном в конце статьи.
Когда таймер обратного отсчета достигнет нуля зуммер начнет издавать звуки (100 раз если его не прервать). Чтобы остановить сигнал зуммера необходимо нажать и удерживать кнопку. Также нажать кнопку можно в любое время если вы хотите остановить таймер.
Защита от помех DC
Раздельное питание
Один из лучших способов защититься от помех по питанию – питать силовую и логическую части от отдельных источников питания: хороший малошумящий источник питания на микроконтроллер и модули/сенсоры, и отдельный на силовую часть. В автономных устройствах иногда ставят отдельный аккумулятор на питание логики, и отдельный мощный – на силовую часть, потому что стабильность и надёжность работы очень важна.
Искрогасящие цепи DC
При размыкании контактов в цепи питания индуктивной нагрузки происходит так называемый индуктивный выброс, который резко подбрасывает напряжение в цепи вплоть до того, что между контактами реле или выключателя может проскочить электрическая дуга (искра). В дуге нет ничего хорошего – она выжигает частички металла контактов, из за чего они изнашиваются и со временем приходят в негодность. Также такой скачок в цепи провоцирует электромагнитный выброс, который может навести в электронном устройстве сильные помехи и привести к сбоям или даже поломке! Самое опасное, что индуктивной нагрузкой может являться сам провод: вы наверняка видели, как искрит обычный выключатель света в комнате. Лампочка – не индуктивная нагрузка, но идущий к ней провод имеет индуктивность. Для защиты от выбросов ЭДС самоиндукции в цепи постоянного тока используют обыкновенный диод, установленный встречно-параллельно нагрузке и максимально близко к ней. Диод просто закоротит на себя выброс, и все дела:
Где VD – защитный диод, U1 – выключатель (транзистор, реле), а R и L схематично олицетворяют индуктивную нагрузку. Диод нужно ОБЯЗАТЕЛЬНО ставить при управлении индуктивной нагрузкой (электромотор, соленоид, клапан, электромагнит, катушка реле) при помощи транзистора, то есть вот так:
При управлении ШИМ сигналом рекомендуется ставить быстродействующие диоды (например серии 1N49xx) или диоды Шоттки (например серии 1N58xx), максимальный ток диода должен быть больше или равен максимальному току нагрузки.
Фильтры
Если силовая часть питается от одного источника с микроконтроллером, то помехи по питанию неизбежны. Простейший способ защитить МК от таких помех – конденсаторы по питанию как можно ближе к МК: электролит 6.3V 470 uF (мкФ) и керамический на 0.1-1 мкФ, они сгладят короткие просадки напряжения. Кстати, электролит с низким ESR справится с такой задачей максимально качественно.
Ещё лучше с фильтрацией помех справится LC фильтр, состоящий из индуктивности и конденсатора. Индуктивность нужно брать с номиналом в районе 100-300 мкГн и с током насыщения больше, чем ток нагрузки после фильтра. Конденсатор – электролит с ёмкостью 100-1000 uF в зависимости опять же от тока потребления нагрузки после фильтра. Подключается вот так, чем ближе к нагрузке – тем лучше:
Подробнее о расчёте фильтров можно почитать здесь.
Работа схемы
Структурно рассматриваемое нами устройство состоит из 4-х модулей: модуль датчиков, модуль управления, модуль отображения и модуль реле.
В модуле датчиков, схема которого представлена на рисунке ниже, мы имеем два инфракрасных датчика с инфракрасными диодами, потенциометр, компаратор (на операционном усилителе) и светодиоды. Потенциометр предназначен для установки опорного напряжения компаратора (для регулировки чувствительности устройства). Инфракрасные датчики обнаруживают отсутствие или присутствие объекта (человека) в поле своего действия и выдают соответствующие сигналы на компаратор. Компаратор сравнивает два напряжения и обеспечивает соответствующий цифровой сигнал на своем выходе. В представленном устройстве мы использовали два компаратора (поскольку у нас два модуля инфракрасных датчиков), реализованных на микросхеме LM358.
Модуль управления построен на основе платы Arduino UNO, которая используется для управления всем устройством. Выходы компараторов подсоединены к цифровым контактам 14 и 19 Arduino. Плата Arduino считывает сигналы с этих цифровых контактов и на их основе выдает соответствующие управляющие сигналы на реле, которое управляет включением и выключением цепи электрической лампочки (bulb).
Модуль отображения состоит из ЖК дисплея 16×2, на котором будет отображаться число людей, находящихся в комнате и статус света (включен/выключен) когда никого нет в комнате.
Модуль реле состоит из транзистора BC547 и реле на 5 В, управляющим включением/выключением электрической лампочки. Транзистор необходим для управления реле поскольку плата Arduino не обеспечивает необходимые значения токов и напряжений для этого. Плата Arduino будет подавать управляющие команды на транзистор, а он с помощью реле будет включать и выключать электрическую лампочку.
Полная схема устройства представлена на следующем рисунке.
Выходы инфракрасных датчиков непосредственно подключены к цифровым контактам Arduino 14(A0) и 19(A5). База транзистора через резистор подключена к контакту 2 Arduino. ЖК дисплей подсоединен к Arduino в 4-битном режиме. Контакты RS и EN ЖК дисплея подключены к контактам 13 и 12 Arduino. Контакты данных ЖК дисплея D4-D7 подключены к контактам 11-8 Arduino.
Схема алкотестера на Ардуино Нано с дисплеем
Схема сборки алкотестера на Arduino Nano своими руками
На данном датчике имеется две группы контактов. Первая группа — это питание и выходной сигнал, вторая группа служит для включения/выключения нагревателя. При замкнутой перемычке на плате MQ3, нагреватель находится всегда во включенном состоянии, поэтому вторая группа контактов не используется. После сборки схемы, согласно приведенной картинке, загрузите следующий код в Arduino Nano.
Скетч алкотестера на Ардуино Нано с дисплеем
#include <TroykaMQ.h> // библиотека для MQ датчиков #include <Wire.h> // библиотека для протокола I2C #include <LiquidCrystal_I2C.h> // библиотека для LCD 1602 LiquidCrystal_I2C LCD(0x27,20,2); // присваиваем имя дисплею MQ3 mq3(A1); void setup() { Serial.begin(9600); // запускаем монитор порта LCD.init(); // инициализация дисплея LCD.backlight(); // включение подсветки mq3.calibrate(); // калибровка датчика MQ3 } void loop() { Serial.print("Alcohol: "); Serial.print(mq3.readAlcoholMgL()); // выводим значение на монитор Serial.println(" mG/L"); Serial.print("Alcohol: "); Serial.print(mq3.readAlcoholPpm()); // выводим значение на монитор Serial.println(" ppm"); LCD.setCursor(0,0); LCD.print("Alcohol: "); LCD.print(mq3.readAlcoholMgL()); // выводим значение на дисплей LCD.print(" mG/L"); LCD.setCursor(0,1); LCD.print("Alcohol: "); LCD.print(mq3.readAlcoholPpm()); // выводим значение на дисплей LCD.print(" ppm"); delay(500); LCD.clear(); // очищаем экран дисплея }
Пояснения к коду:
- как и сенсор MQ-2 Arduino, датчик выводит информацию о концентрации паров спирта в долях на миллион (ppm), а также в миллиграммах на литр (mG/L);
- оба скетча будут работать на разных платах — Uno и Nano. Главное установить библиотеку TroykaMQ.h, которую можно скачать на нашем сайте здесь.