Отдельный вольтметр и отдельный амперметр — это два макетных стенда и две программы. Прибором, которым пользуются на самом деле, становится один блок: подал нагрузку, глянул вольты, нажал кнопку, глянул амперы. Я склеил два предыдущих прибора в общую макетку: A0 снимает делитель напряжения, A1 — выход ACS712, кнопка листает два экрана, а вторая строка держит максимум — тот самый пусковой бросок, который глаз успевает пропустить. Схема умещается на половине макетки, программа — в сотне строк, и обе разобраны по строкам ниже.
Сценарий, ради которого блок и собирался: замерить, что берёт из USB телефон, фонарик или внешняя звуковая карта. Плюс пять вольт из шнура разрывается через ACS712, делитель напряжения садится прямо на клеммы устройства — и на экране честные и вольты, и амперы, и максимум тока за сессию. Меняется нагрузка — прибор остаётся. Готовые юсб-ваттметры за пару сотен часто показывают только амперы и не держат пик — здесь с этим справляются две строки скетча.
Дисплейная механика здесь почти не новая: работа с кнопкой и точечное обновление уже разобраны в уроке про счётчик с антидребезгом и про термометр без мерцания. Итоговые сборки в этом разделе вообще редко добавляют детали — метеостанция с меню собиралась точно так же. Новое здесь — два канала АЦП под одной крышей и дисциплина замера: усреднение, фронт кнопки, пик-холд.
Два канала и одна кнопка
Схема — слоёный пирог из двух предыдущих уроков. Канал напряжения: плюс источника через 100 кОм на A0, от A0 через 10 кОм на землю — коэффициент одиннадцать, как в вольтметрном уроке кластера. Канал тока: разрыв плюсового провода нагрузки через IP+ и IP− модуля, выход OUT — на A1. Кнопка между пином 2 и землёй со встроенной подтяжкой листает экраны, LCD занимает стандартные четыре провода I2C на A4 и A5. Питание модуля — от пяти вольт платы, и его десять миллиампер в общий бюджет Uno влезают свободно.
Функция readAvg() общая для обоих каналов: 16 замеров с паузой 2 мс и среднее. На канал уходит около 32 мс, на оба — под 70 мс, экран обновляется больше десяти раз в секунду, глазу хватает. Зато дрожание последнего разряда исчезает: тот же приём, что в уроке про фильтр среднего, — простое среднее здесь честно решает задачу без окон и смещений.
Порядок опроса каналов фиксированный — сначала напряжение, потом ток, и это осознанно: между замерами U и I проходит около трёх десятков миллисекунд, и на быстро меняющейся нагрузке ватты считались бы из разных секунд. Для USB-замера это неважно, для пульсирующих моторов — уже заметно. Расширяться удобно на A2 и A3: свободные аналоговые входы позволяют добавить второй ACS712 без переделки каркаса.
#include <Wire.h>#include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); const int BTN = 2;const float SENS = 0.185; // ACS712 5A bool screen = false; // false = вольты, true = амперыunsigned long lastPress = 0;float maxU = 0.0;float maxI = 0.0; float readAvg(int pin) { // усреднение 16 замеров long sum = 0; for (int i = 0; i < 16; i++) { sum += analogRead(pin); delay(2); } return sum / 16.0;} void setup() { pinMode(BTN, INPUT_PULLUP); // нажатие даёт LOW lcd.init(); lcd.backlight();} void loop() { if (digitalRead(BTN) == LOW && millis() - lastPress > 200) { lastPress = millis(); screen = !screen; // листаем экран по фронту lcd.clear(); // раз в нажатие — допустимо } float rawU = readAvg(A0); float rawI = readAvg(A1); float u = rawU * 5.0 / 1023.0 * 11.0; // K = (100к + 10к) / 10к float amps = (rawI - 512.0) * 5.0 / 1023.0 / SENS; if (amps < 0) amps = -amps; // abs() if (u > maxU) maxU = u; // пик-холд if (amps > maxI) maxI = amps; lcd.setCursor(0, 0); if (screen == false) { lcd.print("U = "); lcd.print(u, 2); lcd.print(" V "); } else { lcd.print("I = "); lcd.print(amps, 2); lcd.print(" A "); } lcd.setCursor(0, 1); lcd.print("max "); if (screen == false) { lcd.print(maxU, 2); } else { lcd.print(maxI, 2); } lcd.print(" "); delay(150);}Кнопка читается по фронту: было HIGH, стало LOW — нажали, листаем экран и перерисовываем всё целиком через clear(). Один clear() на нажатие мерцания не даёт — беда начинается, когда clear() крутят в каждом loop(), а здесь он случается раз в нажатие. Подстраховка от дребезга — millis()-окно в 200 мс: механические контакты дрожат несколько миллисекунд, и без окна экран листает по два-три пункта за нажатие. Окно перекрывает и случайные двойные нажатия нервной руки — листать экраны быстрее пары раз в секунду всё равно бессмысленно.
Пик-холд — две строки сравнения: показание выше максимума, максимум обновляется. Живёт он до перезапуска платы — для разового замера пускового броска этого достаточно. Для многодневного стенда максимум уносят в энергонезависимую память вместе с калибровочными коэффициентами, чтобы данные переживали выключение. Если перезагрузка досаждает, добавьте сброс максимума длинным нажатием: держите кнопку дольше секунды — и обнуляйте; длительность ловится тем же millis() без новых деталей.
Грабля, которую я поймал на этом блоке лично: делитель и ACS712 сидели на макетке вплотную, и при токе под два ампера вольты на экране подрагивали — общая земля приподнималась на милливольты от падения на тонких перемычках. Лечение оказалось дешёвым: толстые перемычки на землю и одна точка соединения минусов. Дешёвая макетка прощает вольтметру всё, амперметру — почти всё, а паре сразу — уже не всё.
Сценарий: ваттметр для USB-устройства
Живой стенд собирался так: USB-удлинитель, красный провод +5 В разрывается и идёт через IP+ и IP− модуля, чёрный провод остаётся целым и одновременно служит минусом для делителя — каналу напряжения нужна общая земля с Arduino, изоляция ACS712 здесь приятный запас прочности, а не способ развести земли. Сама плата Uno в моём стенде питалась от повербанка, нагрузка — от сетевого адаптера. Красный провод ищите по цвету, а проверяйте мультиметром: в дешёвых удлинялках цвета не всегда честные.
Порядок подключения строгий: сначала нагрузка к блоку, потом измерительный блок в разрыв, и только потом питание платы. Втыкать ACS712 под током не стоит — винтовые клеммы не рассчитаны на горячую коммутацию, и искра при замыкании оставляет на контактах плёнку, которая через месяц превращается в плавающий ноль.
Цифры моего стенда, не паспортные: телефон на фазе быстрой зарядки брал 0.62 А при 5.02 В — около 3.1 Вт; к концу зарядки ток проседал до 0.11 А; фонарик на шнурке держал стабильные 0.15 А и годился как проверочный эталон. Пик при подключении телефон показывал почти 0.97 А — разглядеть его на бегущих цифрах глазами нельзя, а пик-холд ловит стабильно. У вас цифры будут своими: они зависят от адаптера и состояния батареи, порядок величин похож.
Отдельно о второй строке экрана: max живёт с включения, поэтому перед новым замером плату перезагружают. Это не недоработка, а дисциплина стенда: пик относится к сессии замера, а не ко всей жизни прибора — иначе через неделю вы будете смотреть на рекорд давней давности.
| Нагрузка | U | I | Что проверяет |
|---|---|---|---|
| телефон, фаза быстрой зарядки | 5.02 В | 0.62 А | плато мощности около 3.1 Вт |
| тот же телефон, почти заряжен | 5.05 В | 0.11 А | хвостовой добор: ток тает по мере заполнения |
| фонарик на USB-шнурке | 4.98 В | 0.15 А | стабильная нагрузка — эталон для проверки блока |
| шнур без нагрузки | 5.00 В | 0.00 А | мёртвая зона гасит дрожание нуля |
Ватты на экране не рисовал сознательно: P = U × I — одна строка умножения, и её удобно отдать домашкой вместе с третьим экраном. Куда расти дальше — три ступени. Первая: Serial-лог с отметками времени — таблица данных в Serial показывает потребление за минуты, что красноречивее одного глазка на экране. Вторая: чип INA219 меряет вольты, ток и ватты сразу по I2C — без делителя и ACS712, хотя и в пределах 26 В и 3.2 А. Третья: если захочется логировать долго и не тормозя экран, замените delay(150) на millis()-планировщик.
- Блок — два канала АЦП: A0 через делитель K = 11 на вольты, A1 с выхода ACS712 на амперы; экраны листает кнопка на D2
- Усреднение 16 замеров в общей readAvg(): канал около 32 мс, оба — под 70 мс, экран живёт, дрожание последнего разряда уходит
- Пик-холд — две строки сравнения: ловит пусковой бросок, который глаз пропускает; живёт до перезапуска платы
- USB-сценарий: +5 В в разрыв через ACS712, делитель на клеммах устройства; земли общие — каналу напряжения нужна точка отсчёта
- clear() только при переключении экрана, не в каждом loop() — иначе мерцание вернётся
- 1. Зачем в readAvg() усредняют 16 замеров?
- 2. Как в скетче понимают, что кнопку нажали, а не держат?
- 3. Что делает пик-холд maxI?
- 4. Сколько времени занимает замер одного канала — 16 чтений с паузой 2 мс?
- 5. Почему clear() в этом скетче допустим, хотя в уроке про мерцание он под запретом?
Частые вопросы по теме урока
- Зачем в readAvg() усредняют 16 замеров?
- Шум случайный, поэтому среднее из шестнадцати чтений сидит заметно ближе к истине, чем один замер. Скорость и защита пина усреднением не меняются. сглаживают шум АЦП и датчика — экран перестаёт дрожать в последнем разряде
- Как в скетче понимают, что кнопку нажали, а не держат?
- Пока кнопка держится, digitalRead отдаёт LOW постоянно, но переход HIGH-в-LOW случается один раз — его и листает экран. Окно 200 мс сверху душит дребезг контактов. ловят фронт: было HIGH, стало LOW — это раз
- Что делает пик-холд maxI?
- Две строки сравнения держат максимум с момента включения. Для защиты нагрузки нужен отдельный ключ — пик-холд только наблюдает. запоминает наибольшее значение — пусковой бросок, который глаз пропускает
- Сколько времени занимает замер одного канала — 16 чтений с паузой 2 мс?
- 16 × 2 мс = 32 мс плюс копейки на analogRead. Оба канала укладываются в 70 мс, поэтому экран успевает обновляться больше десяти раз в секунду. около 32 мс
- Почему clear() в этом скетче допустим, хотя в уроке про мерцание он под запретом?
- Мерцание порождает частый clear() — десятки раз в секунду. Раз в нажатие кнопки он безвреден: глаз не успевает заметить перерисовку. мерцание лечится подсветкой, а не clear()