Руководства · 12 мин чтения

Вольтметр на Arduino: измерение 0–25 V, LCD и калибровка

Как собрать вольтметр на Arduino: измерение 0–25 V с делителем, подбор резисторов, защита стабилитроном, калибровка, LCD, INA219 и тестер батарей.

Вольтметр на Arduino — измерительный прибор, который можно собрать за вечер: двух резисторов, одного аналогового входа и нескольких строк кода достаточно, чтобы точно измерять 0–25 V и выводить значение на дисплей. Аналоговые входы Arduino могут считывать не более 5 V, поэтому для измерения более высокого напряжения его сначала уменьшают делителем напряжения, а затем в коде увеличивают результат в то же число раз. На этом принципе работают мониторы аккумуляторов, индикаторы блоков питания, регистраторы солнечных панелей и тестеры батарей.

В этом руководстве ты шаг за шагом узнаешь, как подобрать номиналы резисторов для нужного диапазона, как стабилитрон и последовательный резистор защищают аналоговый вход от перенапряжения, как работает готовый модуль датчика напряжения 0–25 V и как настройка опорного напряжения и калибровка приближают показания к результатам мультиметра. Затем выведешь значение на LCD с I2C, соберёшь такой же вольтметр на ESP32 и Raspberry Pi Pico, добавишь измерение тока с INA219, превратив его в ваттметр, и в конце сделаешь тестер, измеряющий реальную ёмкость батареи в mAh.

Цифровой вольтметр
Вольтметр на Arduino может показывать напряжение с двумя знаками после запятой, как эти панельные приборы.

Как работает вольтметр на Arduino?

Arduino не определяет напряжение напрямую: её АЦП (аналого-цифровой преобразователь) сравнивает напряжение на аналоговом входе с опорным и возвращает результат в виде целого числа. АЦП Arduino Uno 10-битный: он выдаёт 0 при 0 V и 1023 при опорном напряжении (по умолчанию 5 V). Напряжение на пине равно Vpin = reading × Vref / 1024. Если на входе стоит делитель напряжения, фактическое измеряемое напряжение равно Vin = Vpin × (R1 + R2) / R2. Таким образом, точность всего вольтметра зависит от трёх вещей: реального значения Vref, соотношения двух резисторов и собственной погрешности АЦП.

ПлатаАЦПОпорное напряжение1 шаг на пине1 шаг с делителем для 0–25 V
Arduino Uno, Nano, Pro Mini10 бит (0–1023)5 V (AVcc)4,88 mV24,4 mV (30 kΩ + 7,5 kΩ)
ESP3212 бит (0–4095)Около 3,3 V (ослабление 11 dB)Около 0,8 mVОколо 9 mV (100 kΩ + 10 kΩ)
Raspberry Pi Pico12 бит (0–65535 через read_u16)3,3 V (ADC_VREF)0,81 mV8,9 mV (100 kΩ + 10 kΩ)

Делитель напряжения и выбор резисторов для 0–25 V

Измеряемое напряжение подаётся через верхний резистор R1, средняя точка соединения двух резисторов идёт на аналоговый пин, а нижний резистор R2 — на GND. При выборе резисторов сначала определи максимальное напряжение, затем подбери соотношение, при котором напряжение на пине будет немного ниже опорного, например около 4,5 V на Uno. Пара 30 kΩ и 7,5 kΩ, используемая в готовых модулях, делит ровно на 5: при 25 V на входе на пине получается 5,00 V. Номиналы тоже нельзя выбирать наугад: при слишком низком общем сопротивлении делитель напрасно расходует ток измеряемого источника, а при слишком высоком конденсатор выборки внутри АЦП не успевает зарядиться, и показания отклоняются. Даташит ATmega328P рекомендует сопротивление источника сигнала на аналоговом входе не более 10 kΩ; для делителя это сопротивление параллельно соединённых R1 и R2 (6 kΩ для 30 kΩ и 7,5 kΩ). Подробное объяснение формулы и влияния нагрузки есть в статье делитель напряжения.

ДиапазонArduino Uno (5 V): R1 + R2На пине при верхнем пределеESP32 / Pico (3,3 V): R1 + R2На пине при верхнем пределе
0–10 V12 kΩ + 10 kΩ4,55 V33 kΩ + 10 kΩ2,33 V
0–25 V30 kΩ + 7,5 kΩ (готовый модуль)5,00 V100 kΩ + 10 kΩ2,27 V
0–30 V56 kΩ + 10 kΩ4,55 V120 kΩ + 10 kΩ2,31 V
0–50 V100 kΩ + 10 kΩ4,55 V220 kΩ + 10 kΩ2,17 V
Чтобы не рассчитывать соотношение для своего диапазона вручную, воспользуйся утилитой делителя напряжения. В столбцах ESP32 и Pico напряжение на пине намеренно оставлено ниже 2,5 V, потому что АЦП ESP32 теряет линейность вблизи верхнего предела. Выбирай металлоплёночные резисторы с допуском 1%; два резистора с допуском 5% в худшем случае могут изменить соотношение примерно на 10%.

Защита: стабилитрон, последовательный резистор и конденсатор

Даже с правильно рассчитанным делителем неверное подключение щупа, выброс в питании или случайное касание 48 V вместо 25 V могут поднять напряжение на пине выше 5 V. Пины ATmega328P допускают максимум напряжение питания плюс 0,5 V. Внутри пина есть защитные диоды к 5 V и GND, но они выдерживают лишь небольшие токи. Хорошая новость: верхний резистор делителя уже ограничивает ток. При R1 = 30 kΩ даже касание 50 V даст ток в пин не более примерно 1,5 mA. Чтобы ещё уменьшить этот ток и безопасно ограничить избыточное напряжение, можно добавить следующие ступени защиты.

  • Последовательный резистор: установи резистор на 4,7–10 kΩ между средней точкой делителя и аналоговым пином. При перенапряжении ток через внутренние диоды пина снизится до микроамперного диапазона.
  • Конденсатор 100 nF: подключи его между аналоговым пином и GND. Он компенсирует повышение сопротивления источника из-за последовательного резистора: при выборке АЦП берёт нужный заряд из этого конденсатора. Также он фильтрует шум и короткие выбросы.
  • Стабилитрон на 5,1 V: подключи стабилитрон (диод Зенера), например 1N4733A или BZX55C5V1, катодом к пину, а анодом к GND. Когда напряжение пытается подняться выше 5,1 V, стабилитрон начинает проводить ток и отводит избыток на GND.
  • Учитывай ток утечки стабилитрона: стабилитрон начинает потреблять небольшой ток чуть ниже напряжения пробоя, и при высоком сопротивлении делителя этот ток занижает показания. Если используешь стабилитрон, держи максимальное напряжение на пине около 4 V либо вместо него поставь диод Шоттки (BAT85) от пина к шине 5 V.
  • Переполюсовка: если подключить щупы наоборот, на пин попадёт отрицательное напряжение. Последовательный резистор ограничит ток; для дополнительной защиты можно поставить второй диод Шоттки анодом к GND, а катодом к пину.
Этот вольтметр предназначен только для постоянного напряжения (DC) и низких напряжений (максимум несколько десятков вольт). Никогда не пытайся измерять сетевые 230 V двумя резисторами: это смертельно опасно и соединяет GND Arduino с электросетью. Для сетевого напряжения используй трансформаторный модуль с гальванической развязкой, например ZMPT101B. Кроме того, минус измеряемого источника должен быть соединён с GND Arduino; без общей земли показания бессмысленны.

Готовый модуль датчика напряжения (0–25 V)

Если не хочется паять, можно взять небольшой красный или синий модуль, который продаётся как «датчик напряжения». Внутри нет ничего волшебного: только делитель из резисторов ровно на 30 kΩ и 7,5 kΩ, винтовой клеммник и трёхконтактный штырьковый разъём. Поскольку коэффициент деления равен 5, с Arduino на 5 V можно измерять 0–25 V, а с платой на 3,3 V — только до 16,5 V. На плате 3,3 V касание 25 V подаст на пин 5 V и повредит его, поэтому для ESP32 и Pico безопаснее собрать свой делитель по таблице выше. Защитного диода на модуле нет, так что меры со стабилитроном или диодом Шоттки из предыдущего раздела нужны и здесь.

Пин модуляФункцияArduino UnoESP32 (свой делитель)Raspberry Pi Pico (свой делитель)
VCC (винтовой клеммник)Плюс измеряемого источникаПлюс источникаВерхний вывод R1Верхний вывод R1
GND (винтовой клеммник)Минус измеряемого источникаМинус источникаМинус источника и GNDМинус источника и GND
SСигнал после деленияA0GPIO 34 (ADC1)GP26 (ADC0, пин 31)
+На большинстве модулей не подключёнОставь неподключённым--
−GND модуляGNDGNDGND (например, пин 33)

Шаг за шагом: твой первый вольтметр на Arduino

  1. Подключи пин S модуля к A0, а пин − к GND. Если собираешь свой делитель, поставь 30 kΩ со стороны источника, 7,5 kΩ со стороны GND, а среднюю точку соедини с A0.
  2. Подключи Arduino к USB, измерь мультиметром напряжение между пином 5V и GND и запиши значение. Напряжение USB часто находится в пределах от 4,7 до 5,1 V.
  3. Введи измеренное значение в константу VREF в коде ниже и загрузи код.
  4. Подключи измеряемый источник к винтовому клеммнику модуля: плюс к VCC, минус к GND. Для первой проверки идеально подойдёт батарейка на 9 V или регулируемый лабораторный блок питания.
  5. Открой «Монитор порта» (Serial Monitor) на скорости 9600 бод. Считай напряжение и одновременно измерь источник мультиметром, затем сравни два значения.
C++ (Arduino IDE)
1// Arduino voltmetre: 0-25 V gerilim sensörü modülü / Arduino voltmeter: 0-25 V voltage sensor module
2// Modül S -> A0, modül - -> GND / module S -> A0, module - -> GND
3// Ölçülen kaynak modülün VCC ve GND klemensine / measured source goes to the module's VCC and GND terminals
4const int SENSE_PIN = A0;
5const float VREF = 5.00;       // 5V pininde ölçtüğün gerilim / voltage you measured on the 5V pin
6const float R1 = 30000.0;      // üst direnç (ohm) / top resistor (ohms)
7const float R2 = 7500.0;       // alt direnç (ohm) / bottom resistor (ohms)
8const float CAL = 1.000;       // kalibrasyon: gerçek / okunan / calibration: real / measured
9const int SAMPLES = 32;        // ortalama için örnek sayısı / samples to average
10
11float readVoltage() {
12  analogRead(SENSE_PIN);                    // ilk okumayı at / discard the first reading
13  unsigned long sum = 0;
14  for (int i = 0; i < SAMPLES; i++) {
15    sum += analogRead(SENSE_PIN);
16    delayMicroseconds(500);
17  }
18  float adc = (float)sum / SAMPLES;         // 0-1023 arası ortalama / average between 0 and 1023
19  float vPin = adc * VREF / 1024.0;         // pindeki gerilim / voltage at the pin
20  return vPin * (R1 + R2) / R2 * CAL;       // bölücüden önceki gerilim / voltage before the divider
21}
22
23void setup() {
24  Serial.begin(9600);
25}
26
27void loop() {
28  float v = readVoltage();
29  if (v < 0.05) v = 0.0;                    // boşta gürültüyü sıfırla / zero the noise when idle
30  Serial.print(F("Gerilim / Voltage: "));
31  Serial.print(v, 2);
32  Serial.println(F(" V"));
33  delay(500);
34}

Код каждый раз усредняет 32 измерения, поэтому колебания АЦП на ±1 шаг в основном исчезают, а последняя цифра стабилизируется. Первое измерение отбрасывается, поскольку конденсатор выборки АЦП ещё хранит заряд от предыдущего канала. Округление значений ниже 0,05 V до нуля убирает случайные показания вроде 0,02–0,04 V, когда ничего не подключено. Помни, что разрешение составляет 24,4 mV: хотя на экране два знака после запятой, последняя цифра меняется шагами по 0,02–0,03 V. Если нужно более высокое разрешение, сузь диапазон: с делителем для 0–10 V один шаг уменьшается до 10,7 mV.

Опорное напряжение и калибровка

Часто говорят, что вольтметры на Arduino «измеряют неправильно», но настоящая причина ошибки обычно в опорном напряжении. Если в коде указано 5,00 V, а плата получает от USB 4,80 V, все показания будут завышены на 4%: батарея с напряжением 12,00 V будет отображаться как 12,50 V. Это напряжение меняется при переходе от настольного компьютера к ноутбуку и от одного USB-кабеля к другому, а при питании платы от внешнего адаптера напряжение задаёт её стабилизатор, и значение снова другое. Есть три способа исправить это: измерить пин 5V и записать значение в код, использовать фиксированное внутреннее опорное напряжение или при каждом измерении заново вычислять напряжение питания самой платы.

МетодКодПреимуществоНа что обратить внимание
По умолчанию (AVcc)analogReference(DEFAULT)Без настройки, полный диапазон 0–5 VПоказания меняются вместе с питанием
Внутреннее опорное напряжение 1,1 VanalogReference(INTERNAL)Стабильно и не зависит от питанияНа разных платах отличается от 1,0 до 1,2 V; коэффициент деления должен быть около 23 (например, 220 kΩ + 10 kΩ)
Внешнее опорное напряжение (AREF)analogReference(EXTERNAL)Лучшая точность с прецизионным источником опорного напряжения, например LM4040Эта строка должна выполниться до подачи напряжения на AREF, иначе возникнет замыкание с внутренним источником опорного напряжения
Измерение питания (readVcc)Код нижеБез дополнительных компонентов, точность сохраняется при изменении USB-питания или напряжения батареиРаботает только на платах AVR (Uno, Nano, Pro Mini); один раз откалибруй значение 1,1 V
C++ (Arduino IDE)
1// Kendi besleme gerilimini ölçen voltmetre (ATmega328P: Uno, Nano, Pro Mini)
2// Voltmeter that measures its own supply (ATmega328P: Uno, Nano, Pro Mini)
3const int SENSE_PIN = A0;
4const float R1 = 30000.0;      // üst direnç / top resistor
5const float R2 = 7500.0;       // alt direnç / bottom resistor
6long bandgapMv = 1100;         // iç referans (mV), kalibrasyonla düzelt / internal reference (mV), fix by calibration
7
8long readVccMv() {
9  // Referans AVcc, ölçülen kanal iç 1,1 V / reference AVcc, measured channel is the internal 1.1 V
10  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
11  delay(2);                                  // referans otursun / let the reference settle
12  ADCSRA |= _BV(ADSC);                       // dönüşümü başlat / start a conversion
13  while (bit_is_set(ADCSRA, ADSC)) {}        // bitmesini bekle / wait until it finishes
14  unsigned int raw = ADCW;                   // 10 bit sonuç / 10-bit result
15  return bandgapMv * 1024L / raw;            // Vcc = 1,1 V x 1024 / okunan / Vcc = 1.1 V x 1024 / reading
16}
17
18float readVoltage(float vcc) {
19  analogRead(SENSE_PIN);                     // kanal değişti, ilk okumayı at / channel changed, discard first
20  unsigned long sum = 0;
21  for (int i = 0; i < 32; i++) sum += analogRead(SENSE_PIN);
22  float vPin = (sum / 32.0) * vcc / 1024.0;
23  return vPin * (R1 + R2) / R2;
24}
25
26void setup() {
27  Serial.begin(9600);
28}
29
30void loop() {
31  float vcc = readVccMv() / 1000.0;
32  float v = readVoltage(vcc);
33  Serial.print(F("Vcc: "));
34  Serial.print(vcc, 3);
35  Serial.print(F(" V   Giris / Input: "));
36  Serial.print(v, 2);
37  Serial.println(F(" V"));
38  delay(500);
39}
  1. Калибровка внутреннего опорного напряжения: запусти код readVcc и сравни значение Vcc в мониторе порта с показанием мультиметра на пине 5V. Новое bandgapMv = 1100 × измеренное / отображаемое. Например, если плата показывает 5,12 V, а мультиметр — 4,98 V, введи 1100 × 4.98 / 5.12 ≈ 1070.
  2. Калибровка по одной точке: подключи известное напряжение ближе к верхней границе диапазона (например, 12 V). CAL = значение мультиметра / значение Arduino. Этот коэффициент также исправляет погрешность соотношения из-за допуска резисторов.
  3. Калибровка по двум точкам: если при низких напряжениях есть постоянное смещение, измерь две точки: низкую (например, 2 V) и высокую (например, 20 V). Наклон = (M2 − M1) / (A2 − A1), смещение = M1 − наклон × A1; результат: реальное значение = наклон × показание + смещение (M — значение мультиметра, A — значение Arduino).
  4. Проверка: после калибровки проверь результат на третьем напряжении в середине диапазона. Аккуратно собранный вольтметр на Uno измеряет 0–25 V с погрешностью менее 1%.

Цифровой вольтметр с LCD

LCD-дисплей 16x2 с I2C
Благодаря плате-переходнику I2C дисплей LCD 16x2 подключается всего четырьмя проводами: GND, VCC, SDA и SCL.

Чтобы вольтметр работал независимо от компьютера, достаточно выводить значение на LCD с I2C. Подключи GND дисплея к GND, VCC к 5V, SDA к A4 и SCL к A5. Установи библиотеку LiquidCrystal I2C (Frank de Brabander) через «Менеджер библиотек» (Library Manager). Адрес дисплея обычно 0x27, на некоторых модулях — 0x3F. Код ниже показывает текущее напряжение в верхней строке, а минимальное и максимальное значения с момента включения — в нижней, так что можно заметить и просадку напряжения батареи во время работы мотора.

C++ (Arduino IDE)
1// LCD ekranlı dijital voltmetre (0-25 V, min/maks) / Digital voltmeter with LCD (0-25 V, min/max)
2// LCD: SDA -> A4, SCL -> A5, VCC -> 5V, GND -> GND; sensör S -> A0 / sensor S -> A0
3#include <Wire.h>
4#include <LiquidCrystal_I2C.h>
5
6LiquidCrystal_I2C lcd(0x27, 16, 2);   // adres 0x27 ya da 0x3F / address 0x27 or 0x3F
7
8const int SENSE_PIN = A0;
9const float VREF = 5.00;              // 5V pininde ölçülen / measured on the 5V pin
10const float RATIO = (30000.0 + 7500.0) / 7500.0;   // = 5,0
11const float CAL = 1.000;              // kalibrasyon katsayısı / calibration factor
12
13float vMin = 99.99;
14float vMax = 0.0;
15
16float readVoltage() {
17  analogRead(SENSE_PIN);
18  unsigned long sum = 0;
19  for (int i = 0; i < 32; i++) sum += analogRead(SENSE_PIN);
20  float v = (sum / 32.0) * VREF / 1024.0 * RATIO * CAL;
21  return v < 0.05 ? 0.0 : v;
22}
23
24void printField(float value) {
25  char buf[8];
26  dtostrf(value, 5, 2, buf);          // sabit genişlik: " 9.87" / fixed width: " 9.87"
27  lcd.print(buf);
28}
29
30void setup() {
31  lcd.init();
32  lcd.backlight();
33  lcd.print(F("Voltmetre 0-25V"));
34  delay(1000);
35  lcd.clear();
36}
37
38void loop() {
39  float v = readVoltage();
40  if (v > 0.5) {                      // boştayken min/maks güncelleme / don't update min/max when idle
41    if (v < vMin) vMin = v;
42    if (v > vMax) vMax = v;
43  }
44
45  lcd.setCursor(0, 0);
46  lcd.print(F("V: "));
47  printField(v);
48  lcd.print(F(" V      "));
49
50  lcd.setCursor(0, 1);
51  lcd.print(F("L"));
52  printField(vMin > 99.0 ? 0.0 : vMin);
53  lcd.print(F("  H"));
54  printField(vMax);
55  lcd.print(F("  "));
56  delay(300);
57}

Сборка вольтметра на ESP32

ESP32 работает от 3,3 V, и её пины не выдерживают больше 3,6 V, поэтому для 0–25 V используй делитель 100 kΩ + 10 kΩ: при 25 V на входе на пине получается 2,27 V. Подключи делитель к одному из пинов ADC1 (GPIO 32–39); пины ADC2 не работают при включённом Wi-Fi. АЦП ESP32 12-битный, но нелинейный по краям диапазона: ниже 0,15 V он почти ничего не видит, а выше 2,5 V погрешность растёт. Функция analogReadMilliVolts() использует калибровочные данные, записанные в микросхему на заводе, и даёт результат в милливольтах гораздо точнее необработанного значения. Чтобы убрать оставшееся смещение, код применяет калибровку по двум точкам из предыдущего раздела с константами GAIN и OFFSET.

C++ (Arduino IDE)
1// ESP32 voltmetre: 100k + 10k bölücü ile 0-25 V / ESP32 voltmeter: 0-25 V with a 100k + 10k divider
2// Kaynak + -> 100k -> GPIO 34 -> 10k -> GND, kaynak - -> GND / source + -> 100k -> GPIO 34 -> 10k -> GND
3const int SENSE_PIN = 34;                 // ADC1_CH6, Wi-Fi açıkken de çalışır / works with Wi-Fi on
4const float RATIO = (100000.0 + 10000.0) / 10000.0;   // = 11,0
5const float GAIN = 1.000;                 // iki noktalı kalibrasyon: eğim / two-point calibration: slope
6const float OFFSET = 0.00;                // iki noktalı kalibrasyon: kayma (V) / two-point calibration: offset (V)
7
8float readVoltage() {
9  uint32_t mv = 0;
10  for (int i = 0; i < 64; i++) {
11    mv += analogReadMilliVolts(SENSE_PIN);   // fabrika kalibrasyonlu mV / factory-calibrated mV
12  }
13  float vPin = mv / 64.0 / 1000.0;           // pindeki gerilim (V) / voltage at the pin (V)
14  float v = vPin * RATIO * GAIN + OFFSET;
15  return v < 0.10 ? 0.0 : v;
16}
17
18void setup() {
19  Serial.begin(115200);
20  analogReadResolution(12);                  // 0-4095
21}
22
23void loop() {
24  float v = readVoltage();
25  Serial.printf("Gerilim / Voltage: %.2f V\n", v);
26  delay(500);
27}

Сборка вольтметра на Raspberry Pi Pico (MicroPython)

На Raspberry Pi Pico аналоговые входы — GP26, GP27 и GP28. Подключи к GP26 такой же делитель 100 kΩ + 10 kΩ, как для ESP32. В MicroPython read_u16() возвращает 12-битный результат, масштабированный до 0–65535. Опорное напряжение АЦП Pico получается фильтрацией шины 3,3 V; для большей точности измерь мультиметром напряжение между ADC_VREF (пин 35) и GND и введи значение в VREF. В коде есть и дополнение: на обычной Pico пин GP29 (ADC3) соединён с внутренним делителем, который делит напряжение входа питания VSYS платы на 3. Поэтому без дополнительных компонентов можно считывать и напряжение батареи, питающей Pico. На Pico W и Pico 2 W пин GP29 совмещён с беспроводной микросхемой, поэтому на этих платах эта строка напрямую не работает.

MicroPython
1# Raspberry Pi Pico voltmetre (MicroPython) / Raspberry Pi Pico voltmeter (MicroPython)
2# Kaynak + -> 100k -> GP26 -> 10k -> GND / source + -> 100k -> GP26 -> 10k -> GND
3from machine import ADC
4import time
5
6sense = ADC(26)            # GP26 = ADC0 (pin 31)
7vsys = ADC(29)             # GP29 = VSYS / 3 (yalnız standart Pico / standard Pico only)
8VREF = 3.3                 # ADC_VREF; multimetreyle ölçüp düzelt / measure and correct with a multimeter
9RATIO = (100000 + 10000) / 10000    # = 11.0
10GAIN = 1.000               # iki noktalı kalibrasyon: eğim / two-point calibration: slope
11OFFSET = 0.00              # iki noktalı kalibrasyon: kayma (V) / two-point calibration: offset (V)
12
13
14def pin_voltage(adc, samples=64):
15    total = 0
16    for _ in range(samples):
17        total += adc.read_u16()            # 0-65535
18    return total / samples * VREF / 65535  # pindeki gerilim / voltage at the pin
19
20
21while True:
22    v_in = pin_voltage(sense) * RATIO * GAIN + OFFSET
23    if v_in < 0.05:
24        v_in = 0.0
25    v_sys = pin_voltage(vsys) * 3          # VSYS = GP29 x 3
26    print("Giris / Input: {:.2f} V   VSYS: {:.2f} V".format(v_in, v_sys))
27    time.sleep(0.5)

Добавляем измерение тока и получаем ваттметр: ACS712 или INA219

Модуль датчика тока ACS712
ACS712 измеряет ток с помощью эффекта Холла и обеспечивает электрическую развязку измеряемой цепи от Arduino.

Если добавить к вольтметру измерение тока, можно вычислять и мощность: P = V × I. Если батарея на 12 V отдаёт 1,5 A, мощность равна 18 W. Суммирование мощности по времени даёт энергию: нагрузка 18 W за 2 часа потребляет 36 Wh. Есть два распространённых варианта. ACS712 имеет аналоговый выход, измеряет также переменный ток и обеспечивает развязку; он просто становится вторым аналоговым каналом уже собранного вольтметра. INA219 — микросхема контроля мощности в цепях постоянного тока, которая измеряет напряжение и ток с 12-битной точностью и обменивается данными по I2C; сама по себе она уже готовый ваттметр.

ХарактеристикаМодуль напряжения + ACS712 (5A)Модуль INA219
Диапазон напряжения0–25 V (задаётся делителем)0–26 V (внутри микросхемы)
Диапазон тока±5 A±3,2 A (с шунтом 0,1 Ω)
Разрешение по токуОколо 26 mA на Uno0,1 mA
Переменный токДаНет
ПодключениеДва аналоговых пинаI2C (SDA, SCL), адрес 0x40
Лучше всего подходит дляМоторы, ток на стороне электросети и задачи, требующие развязкиИзмерение небольшой мощности в цепях постоянного тока: батареи, солнечные панели и USB-устройства
C++ (Arduino IDE)
1// INA219 + I2C LCD ile wattmetre: V, A, W ve Wh / Wattmeter with INA219 + I2C LCD: V, A, W and Wh
2// Kaynak + -> INA219 VIN+ ; INA219 VIN- -> yük + ; yük - -> kaynak - -> Arduino GND
3// Source + -> INA219 VIN+ ; INA219 VIN- -> load + ; load - -> source - -> Arduino GND
4#include <Wire.h>
5#include <Adafruit_INA219.h>
6#include <LiquidCrystal_I2C.h>
7
8Adafruit_INA219 ina219;               // varsayılan adres 0x40 / default address 0x40
9LiquidCrystal_I2C lcd(0x27, 16, 2);
10
11float energyWh = 0.0;
12unsigned long lastMs = 0;
13
14void printField(float value, int width, int decimals) {
15  char buf[10];
16  dtostrf(value, width, decimals, buf);
17  lcd.print(buf);
18}
19
20void setup() {
21  Serial.begin(9600);
22  lcd.init();
23  lcd.backlight();
24  if (!ina219.begin()) {              // modül bulunamadı / module not found
25    lcd.print(F("INA219 yok!"));
26    while (true) delay(10);
27  }
28  // ina219.setCalibration_16V_400mA();  // küçük akımlar için daha hassas / finer for small currents
29  lastMs = millis();
30}
31
32void loop() {
33  float busV = ina219.getBusVoltage_V();           // VIN- ile GND arası / between VIN- and GND
34  float shuntMv = ina219.getShuntVoltage_mV();     // şönt üzerindeki düşüm / drop across the shunt
35  float loadV = busV + shuntMv / 1000.0;           // kaynağın gerilimi / source voltage
36  float currentA = ina219.getCurrent_mA() / 1000.0;
37  float powerW = loadV * currentA;
38
39  unsigned long now = millis();
40  energyWh += powerW * (now - lastMs) / 3600000.0; // W x saat = Wh / W x hours = Wh
41  lastMs = now;
42
43  lcd.setCursor(0, 0);
44  printField(loadV, 5, 2);  lcd.print(F("V "));
45  printField(currentA, 6, 3); lcd.print(F("A  "));
46  lcd.setCursor(0, 1);
47  printField(powerW, 6, 2); lcd.print(F("W "));
48  printField(energyWh, 6, 2); lcd.print(F("Wh"));
49
50  Serial.print(loadV, 2);    Serial.print(F(" V, "));
51  Serial.print(currentA, 3); Serial.print(F(" A, "));
52  Serial.print(powerW, 2);   Serial.print(F(" W, "));
53  Serial.print(energyWh, 3); Serial.println(F(" Wh"));
54  delay(500);
55}

INA219 вычисляет ток по небольшому падению напряжения на шунтирующем резисторе 0,1 Ω между VIN+ и VIN−, поэтому модуль включается последовательно в плюсовую цепь источника (в верхнем плече). Поскольку напряжение шины измеряется между VIN− и GND, код добавляет падение на шунте, чтобы получить реальное напряжение источника. По умолчанию калибровка рассчитана на 32 V и 2 A; если нужно измерять лишь несколько сотен миллиампер, включи строку setCalibration_16V_400mA(), чтобы сузить диапазон до 400 mA и повысить точность на малых токах. Энергия вычисляется суммированием текущей мощности, умноженной на время, прошедшее за каждый цикл; при выключении Arduino она сбрасывается, поэтому для сохранения периодически записывай значение в EEPROM. Код с таким же расчётом для ACS712 есть в разделе о мощности и энергии статьи об ACS712.

Тестер батарей: вольтметр, измеряющий ёмкость (mAh)

Ячейка Li-ion 18650
Ёмкость, указанная на ячейке 18650, часто отличается от реальной; тестер покажет правду.

Напряжение батареи не показывает точно, сколько энергии в ней осталось: полностью заряженная, но изношенная ячейка тоже может показывать 4,1 V. Чтобы узнать реальную ёмкость, нужно разрядить батарею через известную нагрузку и суммировать ток по времени: mAh = Σ (ток × время). В этой схеме MOSFET (МОП-транзистор) включает и выключает нагрузку, а Arduino раз в секунду измеряет напряжение батареи и напряжение на нижнем выводе нагрузочного резистора. Разность этих напряжений, делённая на сопротивление нагрузки, даёт ток. Когда батарея достигает напряжения отсечки, MOSFET закрывается, и результат выводится на экран. В начале код также сравнивает напряжение батареи без нагрузки и под нагрузкой, чтобы вычислить её внутреннее сопротивление; чем оно выше, тем сильнее проседает напряжение батареи при большом токе.

КомпонентПодключениеПримечание
Плюс батареиК A0 через 10 kΩ; также к верхнему выводу нагрузочного резистораУстанови 100 nF между A0 и GND
Нагрузочный резистор 4,7 ΩМежду плюсом батареи и стоком MOSFETЦементный резистор минимум на 5 W (лучше 10 W); с 18650 через него течёт около 0,9 A, и он нагревается
Сток MOSFETНижний вывод нагрузочного резистора; к A1 через 10 kΩМежду A1 и GND тоже установи 100 nF
Исток MOSFETGNDМинус батареи тоже подключается сюда
Затвор MOSFETК D9 через 220 Ω; к GND через 100 kΩИспользуй MOSFET с логическим уровнем управления, например IRLZ44N
C++ (Arduino IDE)
1// Pil kapasite test cihazı: mAh, Wh ve iç direnç / Battery capacity tester: mAh, Wh and internal resistance
2// Pil + -> 10k -> A0 ; pil + -> 4,7 ohm yük -> MOSFET drain ; drain -> 10k -> A1
3// Battery + -> 10k -> A0 ; battery + -> 4.7 ohm load -> MOSFET drain ; drain -> 10k -> A1
4// MOSFET source -> GND ; gate <- D9 (220 ohm), gate -> 100k -> GND ; pil - / battery - -> GND
5const int BAT_PIN = A0;
6const int DRAIN_PIN = A1;
7const int LOAD_PIN = 9;
8const float VREF = 5.00;        // 5V pininde ölçülen / measured on the 5V pin
9const float R_LOAD = 4.7;       // yük direnci, multimetreyle ölç / load resistor, measure it
10const float CUTOFF_V = 3.00;    // Li-ion 3,00 V ; alkalin AA / alkaline AA 0,90 V
11
12float mAh = 0.0;
13float wh = 0.0;
14unsigned long lastMs = 0;
15unsigned long startMs = 0;
16bool running = false;
17
18float readPin(int pin) {
19  analogRead(pin);                          // kanal değişti, ilk okumayı at / discard first reading
20  unsigned long sum = 0;
21  for (int i = 0; i < 32; i++) sum += analogRead(pin);
22  return (sum / 32.0) * VREF / 1024.0;
23}
24
25float loadCurrent(float vBat) {
26  return (vBat - readPin(DRAIN_PIN)) / R_LOAD;   // dirençteki gerilim / R / voltage across R / R
27}
28
29void setup() {
30  Serial.begin(9600);
31  pinMode(LOAD_PIN, OUTPUT);
32  digitalWrite(LOAD_PIN, LOW);              // yük kapalı / load off
33  delay(1000);
34
35  float vOpen = readPin(BAT_PIN);           // yüksüz gerilim / open-circuit voltage
36  if (vOpen < CUTOFF_V + 0.1) {
37    Serial.println(F("Pil zayif ya da takili degil / Battery low or missing"));
38    return;
39  }
40  digitalWrite(LOAD_PIN, HIGH);             // yükü aç / load on
41  delay(1000);
42  float vLoad = readPin(BAT_PIN);
43  float amps = loadCurrent(vLoad);
44  Serial.print(F("Bos / Open: "));
45  Serial.print(vOpen, 3);
46  Serial.print(F(" V  Yuklu / Loaded: "));
47  Serial.print(vLoad, 3);
48  Serial.print(F(" V  Ic direnc / Internal R: "));
49  Serial.print((vOpen - vLoad) / amps * 1000.0, 0);
50  Serial.println(F(" mOhm"));
51
52  running = true;
53  startMs = lastMs = millis();
54}
55
56void loop() {
57  if (!running) return;
58  unsigned long now = millis();
59  if (now - lastMs < 1000) return;          // saniyede bir ölç / measure once per second
60
61  float hours = (now - lastMs) / 3600000.0;
62  lastMs = now;
63  float vBat = readPin(BAT_PIN);
64  float amps = loadCurrent(vBat);
65  mAh += amps * 1000.0 * hours;             // akım x süre / current x time
66  wh += vBat * amps * hours;
67
68  Serial.print((now - startMs) / 1000);
69  Serial.print(F(" s  "));
70  Serial.print(vBat, 3);
71  Serial.print(F(" V  "));
72  Serial.print(amps, 3);
73  Serial.print(F(" A  "));
74  Serial.print(mAh, 0);
75  Serial.println(F(" mAh"));
76
77  if (vBat <= CUTOFF_V) {                   // kesme gerilimi / cut-off voltage
78    digitalWrite(LOAD_PIN, LOW);
79    running = false;
80    Serial.print(F("Bitti / Done: "));
81    Serial.print(mAh, 0);
82    Serial.print(F(" mAh, "));
83    Serial.print(wh, 2);
84    Serial.println(F(" Wh"));
85  }
86}

С нагрузкой 4,7 Ω ячейка 18650 разряжается током, который начинается примерно с 0,9 A и снижается к 0,65 A; проверка исправной ячейки на 2 500 mAh занимает около трёх часов. Для Li-ion никогда не задавай напряжение отсечки ниже 3,0 V, иначе ячейка повредится. Если проверяешь щелочную батарейку AA, задай CUTOFF_V равным 0,9 V; с той же нагрузкой ток будет около 0,3 A. Результат можно сравнить с маркировкой и с калькулятором времени работы от батареи. Внутреннее сопротивление новой 18650, измеренное этим способом с учётом держателя и проводов, обычно ниже 0,15 Ω; ячейку с сопротивлением выше 0,3 Ω не следует использовать в задачах с большим током. Чтобы разобраться в единицах ёмкости, загляни в статью mAh и ёмкость батареи.

Никогда не замыкай ячейки Li-ion накоротко и не клади нагрузочный резистор на горючую поверхность: резистор, рассеивающий около 4 W, может нагреться до 80–100 °C. Не оставляй проверку без присмотра и никогда не проверяй вздутые или помятые ячейки.

Идеи проектов с вольтметром на Arduino

  • Контроль аккумулятора и сигнализация низкого напряжения: следи за аккумулятором на 12 V в автодоме или солнечной системе и включай зуммер, когда напряжение упадёт ниже 11,8 V.
  • Регистратор солнечной панели: раз в минуту записывай напряжение панели и её ток, измеренный INA219, на SD-карту и вычисляй суточную выработку в Wh.
  • Индикатор лабораторного блока питания: добавь вольтметр и INA219 на выход модуля LM2596, чтобы превратить его в лабораторный блок питания.
  • Удалённый контроль аккумулятора: отправляй напряжение, измеренное ESP32, в Home Assistant по MQTT или получай сообщение в Telegram при низком напряжении.
  • Проверка сборки 18650: проверь ёмкость ячеек, которые заряжаешь с помощью TP4056, затем подбери ячейки близкой ёмкости для сбалансированной сборки.
  • Индикатор с крупными цифрами: выводи значение на 7-сегментный индикатор или OLED-дисплей вместо LCD такими крупными цифрами, чтобы их было видно с другого конца комнаты.

Частые ошибки и их решения

СимптомВероятная причинаРешение
Показания постоянно завышены или занижены на 2–5%В коде VREF равен 5,00 V, но плата питается другим напряжениемИзмерь пин 5V и внеси значение в VREF либо используй метод readVcc
Значение постоянно скачетМинус источника не соединён с GND Arduino или на линии есть помехиСоедини общую GND, усредняй измерения и добавь 100 nF у пина
При высоком напряжении показания застывают на одном значенииНапряжение на пине достигло опорного, и АЦП вошёл в насыщение на 1023Расширь диапазон измерения, увеличив коэффициент деления
В верхней части диапазона показания искажаются и нелинейныТок утечки стабилитрона или верхняя часть диапазона АЦП ESP32Держи напряжение на пине ниже 4 V (2,5 V на ESP32) или используй диод Шоттки
На ESP32 при включении Wi-Fi показание становится 0 или застываетДелитель подключён к пину ADC2Используй один из пинов ADC1: GPIO 32–39
INA219 показывает отрицательный токVIN+ и VIN− перепутаныПлюс источника подключи к VIN+, плюс нагрузки — к VIN−
Подсветка LCD горит, но текста нетНеверный I2C-адрес или настройка контрастаНайди адрес сканером I2C и поверни подстроечный резистор на обратной стороне
Прежде чем подключать новый делитель к Arduino, включи источник и измерь напряжение в средней точке мультиметром. Если оно близко к расчётному и ниже опорного, можно безопасно подключать его к пину. Эта десятисекундная проверка не даст двум перепутанным резисторам сжечь твою плату.

Вольтметр на Arduino — один из лучших способов понять принцип измерения: два резистора, одна формула и небольшая калибровка дают результаты, близкие к мультиметру; добавь датчик тока — получится ваттметр, добавь MOSFET — тестер батарей. В приложении BlueGrays проект «Цифровой вольтметр с LCD» позволяет шаг за шагом собрать ту же схему по схеме подключения с готовым кодом, а затем перейти к проектам контроля аккумуляторов и измерения мощности.

Частые вопросы

До какого напряжения может измерять Arduino?

Напрямую на аналоговый пин можно подать максимум 5 V (3,3 V на ESP32 и Pico). Делителем напряжения этот предел можно повысить до нужного значения: 30 kΩ + 7,5 kΩ позволяют измерять 25 V, а 100 kΩ + 10 kΩ — 50 V. Однако выше нескольких десятков вольт важны безопасность и изоляция, а сетевое напряжение таким способом измерять нельзя.

Почему вольтметр на Arduino показывает неверное напряжение?

Чаще всего причина в опорном напряжении: в коде указано 5,00 V, но плата получает от USB 4,7–4,9 V, и все показания смещаются в одной и той же пропорции. Другие причины — резисторы с допуском 5%, отсутствие общей GND и слишком высокое сопротивление делителя. Проблему решают измерение напряжения на пине 5V и ввод значения в код либо метод readVcc, а затем настройка коэффициента CAL по известному напряжению.

Можно ли использовать модуль датчика напряжения 0–25 V с ESP32?

Можно, но с осторожностью. Поскольку коэффициент деления модуля равен 5, с ESP32 получится измерять только до 16,5 V; при касании 25 V на пин попадёт 5 V, что повредит ESP32. Собрать собственный делитель 100 kΩ + 10 kΩ для ESP32 и безопаснее, и точнее, поскольку АЦП остаётся в линейной области.

Может ли Arduino измерять сетевое переменное напряжение 230 V?

С делителем напряжения — ни в коем случае: это смертельно опасно и соединяет Arduino с электросетью. Для сетевого напряжения используй модуль вроде ZMPT101B с небольшим трансформатором и гальванической развязкой. На его выходе переменное напряжение, поэтому код вычисляет действующее значение (RMS).

Может ли Arduino измерять отрицательное напряжение?

Напрямую нет: аналоговый пин не может считывать напряжение ниже GND и может повредиться. Решение — сдвинуть сигнал в сторону 5 V с помощью резисторов: например, два одинаковых резистора между сигналом и 5 V создают среднюю точку, в которой диапазон от −5 V до +5 V преобразуется в 0–5 V. Для более точных задач используют преобразователь уровней на ОУ.

Может ли вольтметр на Arduino быть таким же точным, как мультиметр?

Откалиброванный вольтметр на Uno измеряет 0–25 V с погрешностью менее 1%, чего достаточно для большинства любительских задач; это близко к недорогому мультиметру. Из-за 10-битного АЦП разрешение в диапазоне 25 V ограничено примерно 24 mV. Для лучших результатов можно использовать внешний 16-битный АЦП, например ADS1115, резисторы с допуском 0,1% и прецизионный источник опорного напряжения.

Все проекты
Измерение освещённости с LDR
#11Новичок

Измерение освещённости с LDR

Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

4.6(81)
139
Измерение освещённости модулем LDR на ESP32
#113Новичок

Измерение освещённости модулем LDR на ESP32

Наблюдение в мониторе порта в реальном времени за сырым значением ADC модуля LDR и относительным процентом освещённости, вычисленным по опорным значениям темноты и света. Этот процент — не измерение в люксах. Учит логике калибровки модуля. Идеально выполнить его перед проектом 10, потому что он нужен для определения порога.

4.6(36)
55
Цифровой вольтметр с LCD-дисплеем
#56Средний
Premium

Цифровой вольтметр с LCD-дисплеем

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

4.5(27)
36
Измерение температуры с NTC и LCD-дисплеем 2x16
#44Новичок
Premium

Измерение температуры с NTC и LCD-дисплеем 2x16

Измерь температуру вокруг с помощью датчика, похожего на крошечную бусинку. Эта деталь, NTC, при нагреве легче пропускает электричество; Arduino читает это изменение, по формуле переводит его в градус

4.5(33)
44
LCD-экран с I2C
#32Новичок
Premium

LCD-экран с I2C

Дай своему Arduino собственный экран и заставь его написать первое сообщение. Двухстрочный LCD-экран подключается всего несколькими проводами; в верхней строке появляется приветствие, затем второе соо

4.8(53)
70
I2C LCD-дисплей (16x2) на ESP32
#119Средний
Premium

I2C LCD-дисплей (16x2) на ESP32

В этом проекте символьный LCD-экран 16x2 подключается к ESP32 по протоколу I2C и выводит текст. Благодаря I2C экраном можно управлять всего двумя пинами данных (SDA и SCL), не тратя 6+ пинов, как при классическом параллельном подключении. Код печатает постоянное сообщение в первой строке и счётчик, растущий каждую секунду, во второй. Кроме того, при запуске работает сканер I2C, который выводит адрес дисплея в монитор порта.

4.9(23)
40
Измерение расстояния с выводом на LCD
#31Продвинутый
Premium

Измерение расстояния с выводом на LCD

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

4.6(45)
59
Регулировка яркости светодиода потенциометром на ESP32 (аналог → PWM)
#115Средний
Premium

Регулировка яркости светодиода потенциометром на ESP32 (аналог → PWM)

В этом проекте яркость светодиода плавно регулируется потенциометром. Пин АЦП ESP32 читает напряжение 0–3,3V с потенциометра, затем это значение превращается в сигнал PWM (широтно-импульсная модуляция) и подаётся на светодиод. В результате при повороте потенциометра светодиод постепенно становится ярче или тусклее. В Arduino Core 3.0+ можно сразу использовать analogWrite(); на ESP32-S2/S3 разрешение АЦП фиксировано — 12 бит.

4.5(19)
32