Вольтметр на 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 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 Mini | 10 бит (0–1023) | 5 V (AVcc) | 4,88 mV | 24,4 mV (30 kΩ + 7,5 kΩ) |
| ESP32 | 12 бит (0–4095) | Около 3,3 V (ослабление 11 dB) | Около 0,8 mV | Около 9 mV (100 kΩ + 10 kΩ) |
| Raspberry Pi Pico | 12 бит (0–65535 через read_u16) | 3,3 V (ADC_VREF) | 0,81 mV | 8,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 V | 12 kΩ + 10 kΩ | 4,55 V | 33 kΩ + 10 kΩ | 2,33 V |
| 0–25 V | 30 kΩ + 7,5 kΩ (готовый модуль) | 5,00 V | 100 kΩ + 10 kΩ | 2,27 V |
| 0–30 V | 56 kΩ + 10 kΩ | 4,55 V | 120 kΩ + 10 kΩ | 2,31 V |
| 0–50 V | 100 kΩ + 10 kΩ | 4,55 V | 220 kΩ + 10 kΩ | 2,17 V |
Защита: стабилитрон, последовательный резистор и конденсатор
Даже с правильно рассчитанным делителем неверное подключение щупа, выброс в питании или случайное касание 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, а катодом к пину.
Готовый модуль датчика напряжения (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 Uno | ESP32 (свой делитель) | Raspberry Pi Pico (свой делитель) |
|---|---|---|---|---|
| VCC (винтовой клеммник) | Плюс измеряемого источника | Плюс источника | Верхний вывод R1 | Верхний вывод R1 |
| GND (винтовой клеммник) | Минус измеряемого источника | Минус источника | Минус источника и GND | Минус источника и GND |
| S | Сигнал после деления | A0 | GPIO 34 (ADC1) | GP26 (ADC0, пин 31) |
| + | На большинстве модулей не подключён | Оставь неподключённым | - | - |
| − | GND модуля | GND | GND | GND (например, пин 33) |
Шаг за шагом: твой первый вольтметр на Arduino
- Подключи пин S модуля к A0, а пин − к GND. Если собираешь свой делитель, поставь 30 kΩ со стороны источника, 7,5 kΩ со стороны GND, а среднюю точку соедини с A0.
- Подключи Arduino к USB, измерь мультиметром напряжение между пином 5V и GND и запиши значение. Напряжение USB часто находится в пределах от 4,7 до 5,1 V.
- Введи измеренное значение в константу VREF в коде ниже и загрузи код.
- Подключи измеряемый источник к винтовому клеммнику модуля: плюс к VCC, минус к GND. Для первой проверки идеально подойдёт батарейка на 9 V или регулируемый лабораторный блок питания.
- Открой «Монитор порта» (Serial Monitor) на скорости 9600 бод. Считай напряжение и одновременно измерь источник мультиметром, затем сравни два значения.
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 V | analogReference(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 |
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}- Калибровка внутреннего опорного напряжения: запусти код readVcc и сравни значение Vcc в мониторе порта с показанием мультиметра на пине 5V. Новое bandgapMv = 1100 × измеренное / отображаемое. Например, если плата показывает 5,12 V, а мультиметр — 4,98 V, введи 1100 × 4.98 / 5.12 ≈ 1070.
- Калибровка по одной точке: подключи известное напряжение ближе к верхней границе диапазона (например, 12 V). CAL = значение мультиметра / значение Arduino. Этот коэффициент также исправляет погрешность соотношения из-за допуска резисторов.
- Калибровка по двум точкам: если при низких напряжениях есть постоянное смещение, измерь две точки: низкую (например, 2 V) и высокую (например, 20 V). Наклон = (M2 − M1) / (A2 − A1), смещение = M1 − наклон × A1; результат: реальное значение = наклон × показание + смещение (M — значение мультиметра, A — значение Arduino).
- Проверка: после калибровки проверь результат на третьем напряжении в середине диапазона. Аккуратно собранный вольтметр на Uno измеряет 0–25 V с погрешностью менее 1%.
Цифровой вольтметр с LCD

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

Если добавить к вольтметру измерение тока, можно вычислять и мощность: 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 на Uno | 0,1 mA |
| Переменный ток | Да | Нет |
| Подключение | Два аналоговых пина | I2C (SDA, SCL), адрес 0x40 |
| Лучше всего подходит для | Моторы, ток на стороне электросети и задачи, требующие развязки | Измерение небольшой мощности в цепях постоянного тока: батареи, солнечные панели и USB-устройства |
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)

Напряжение батареи не показывает точно, сколько энергии в ней осталось: полностью заряженная, но изношенная ячейка тоже может показывать 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 |
| Исток MOSFET | GND | Минус батареи тоже подключается сюда |
| Затвор MOSFET | К D9 через 220 Ω; к GND через 100 kΩ | Используй MOSFET с логическим уровнем управления, например IRLZ44N |
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 и ёмкость батареи.
Идеи проектов с вольтметром на 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 — один из лучших способов понять принцип измерения: два резистора, одна формула и небольшая калибровка дают результаты, близкие к мультиметру; добавь датчик тока — получится ваттметр, добавь 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
Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

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

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

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

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

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

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

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

