Измерение тока, напряжения и мощности с помощью амперметра, вольтметра и ваттметра соответственно, является привычным делом для радиолюбителей. Но измерение эффективности использования мощности (выхода мощности, отдачи мощности, в англ. – power efficiency) с помощью обычного мультиметра может стать ощутимой проблемой. Поэтому в данной статье мы рассмотрим создание измерителя мощности (ваттметра) на основе модуля ESP32, с помощью которого можно будет измерять входные и выходные напряжения и ток и, следовательно, определять энергетическую эффективность устройства. Ранее на нашем сайте мы также рассматривали проект ваттметра на основе платы Arduino.
Таким образом, вместо того чтобы покупать четыре измерительных устройства, мы сможем объединить возможности всех этих устройств в одном нашем проекте, что приведет к значительной экономии наших денежных средств. Также рассматриваемый здесь проект ваттметра имеет значительный потенциал для дальнейших усовершенствований и улучшений. Например, измеряемые нами данные мы легко можем публиковать в сети Интернет, поскольку основой нашего проекта является модуль ESP32, имеющий возможность подключения к интернету с помощью технологии Wi-Fi.
Примечание: рассматриваемый в данной статье проект ваттметра предназначен для работы в сетях постоянного тока (DC). Если вам необходимо аналогичное устройство для сетей переменного тока (AC), то тогда вам нужно немного изменить проект умного измерителя энергии на основе платы Arduino и модуля ESP12, ранее рассмотренный на нашем сайте. Также на нашем сайте есть проект аналогичного умного измеритель энергии на основе платы Raspberry Pi.
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress).
- OLED дисплей 128X64 (купить на AliExpress).
- Микросхема датчика тока ACS712-20 – 2 шт. (купить на AliExpress).
- Разъем для подключения постоянного тока (DC Barrel Jack).
- Конденсатор 100 мкФ – 2 шт. (купить на AliExpress).
- Конденсатор 104 пФ – 2 шт. (купить на AliExpress).
- Конденсатор 102 пФ – 2 шт. (купить на AliExpress).
- Резистор 10 кОм, 1% – 4 шт.
- Резистор 68 кОм, 1% – 2 шт.
- Резистор 6,8 кОм, 1% – 2 шт.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Внешний вид компонентов, необходимых для рассматриваемого нами проекта ваттметра, приведен на следующем рисунке.
Схема проекта
Схема измерителя мощности (ваттметра) на ESP32 представлена на следующем рисунке.
Принцип работы схемы достаточно прост. Мы будем измерять напряжение и ток на входе и выходе устройства, таким образом, мы сможем определить энергетическую эффективность устройства, которая очень важна (обязательна), к примеру, для конвертеров постоянного тока (DC to DC converter).
Микросхема датчика тока ACS712
Для измерения тока в нашем проекте мы будем использовать микросхему ACS712. Это достаточно интересная микросхема, использующая для измерения тока эффект Холла. Существуют три варианта данной микросхемы – на 5A, 20A и 30A. В нашем проекте мы будем использовать ее вариант на 20A – в данном случае эта микросхема имеет обозначение ACS712-20.
Даташит на ACS712 рекомендует для нормального функционирования микросхемы использовать диапазон напряжений 4.5–5.5 В. Поскольку мы используем модуль ESP32, работающий с напряжением 3.3V, мы использовали делитель напряжения из двух резисторов 10 кОм, чтобы уменьшить уровень напряжения с микросхемы ACS712 перед подачей его на контакт модуля ESP32. Когда через микросхему ACS712 не протекает никакого тока, на ее выходе присутствует напряжение 2.5V. Если же через микросхему ACS712 начинает протекать ток, то напряжение на выходе микросхемы увеличивается или уменьшается в зависимости от направления протекающего через нее тока.
В нашем проекте мы использовали две микросхемы ACS712 – для измерения тока на входе и на выходе устройства.
Делитель напряжения
Для измерения входного и выходного напряжений мы использовали два делителя напряжения – на входе и выходе сети. Максимальное напряжение, которое мы сможем измерять – 35V, но его легко изменить, используя другие номиналы резисторов в делителях напряжения.
Регулятор напряжения
Для подачи питания на модуль ESP32, OLED дисплей и микросхемы ACS712 мы используем регулятор напряжения LM7805. Поскольку элементы нашей схемы мы запитываем от стабильного напряжения, то у нас нет необходимости использования развязывающих конденсаторов, но мы используем конденсаторы 100 мкФ на входе и выходе регулятора напряжения LM7805 для стабилизации его работы.
Модуль ESP32 и OLED дисплей
В качестве основного вычислительного устройства в нашей схеме мы используем модуль ESP32, который будет отвечать за считывание данных, расчеты и вывод данных на экран OLED дисплея 128X64.
Изготовление печатной платы для ваттметра
Автор проекта (ссылка на оригинал приведена в конце статьи) использовал одностороннюю печатную плату. Ее он спроектировал в редакторе Eagle. Дизайн получившейся у него печатной платы показан на следующем рисунке.
Скачать дизайн печатной платы проекта и GERBER файлы для ее изготовления можно по следующей ссылке.
Изначально, для тестирования проекта, его автор изготовил самодельную печатную плату, внешний вид которой показан на рисунке ниже. С помощью этой платы он устранил ряд недочетов в схеме проекта. В финальной версии печатной платы (ссылка на ее скачивание приведена выше) этих недочетов уже нет.
Объяснение программы для модуля ESP32
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
В программе нам необходимо считывать аналоговое напряжение с контактов 33 и 35 модуля ESP32. Также мы будем считывать напряжение с его контактов 32 и 34 – оно нам будет необходимо для определения величины тока. Зная величины тока и напряжения на входе и выходе устройства мы можем определить значения мощности на входе и выходе и, следовательно, рассчитать значение энергетической эффективности устройства. Отображать результаты измерений мы будем на экране OLED дисплея.
Поскольку мы используем OLED дисплей с драйвером SSD1306 и разрешением 128X64, то для взаимодействия с ним нам будет необходимы библиотеки Adafruit_GFX и Adafruit_SSD1306. Скачать их можно по следующим ссылкам:
Также их можно скачать и установить с помощью менеджера библиотек в Arduino IDE.
Первым делом в программе мы подключим все необходимые библиотеки, инициализируем все необходимые контакты и переменные.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <math.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define INPUT_VOLTAGE_SENSE_PIN 33 #define OUTPUT_VOLTAGE_SENSE_PIN 35 #define INPUT_CURRENT_SENSE_PIN 32 #define OUTPUT_CURRENT_SENSE_PIN 34 double R1_VOLTAGE = 68000; //68K double R2_VOLTAGE = 6800; // 6.8K double R1_Current 10000 //10K double R2_Current 22000 //22K //We have used 50 as mVperp value; please refer to the documentation for further information double mVperAmp = 50; // use 100 for 20A Module and 66 for 30A Module double ACSoffset = /*1130*/ 1136; // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); |
Переменные SCREEN_WIDTH и SCREEN_HEIGHT используются для задания размера экрана. Также мы используем переменные со значениями всех резисторов, которые используются в нашей схеме.
Поскольку для измерения тока мы используем микросхему ACS712, мы будем применять переменную mVperAmp для расчета значения тока исходя из значения напряжения. Поскольку мы используем модуль ACS712 на 20A, то необходимо использовать значение mV/A равное 100 как указано в даташите на датчик. Но так как мы используем ESP32 и делитель напряжения, то нам нужно использовать половину этого значения, то есть значение 50, и именно это значение мы присвоили переменной mVperAmp.
ACSoffset – это сдвиг (смещение), который необходим для расчета значения тока из значения напряжения. Поскольку мы микросхему ACS712 запитываем от 5V, то сдвиг напряжения составляет 2.5V. Но так как мы используем делитель напряжения, то этот сдвиг напряжения уменьшается до значения 1.25V. Но, поскольку АЦП (аналого-цифровой преобразователь) у модуля ESP32 не отличается особой точностью, то автор проекта (ссылка на оригинал приведена в конце статьи) использовал для сдвига значение 1136.
Также мы создаем объект класса Adafruit_SSD1306 для работы с OLED дисплеем, передавая в него значения высоты и ширины экрана, конфигурацию I2C, а параметр -1 используется для обеспечения функционала сброса дисплея. Этот параметр особенно полезен когда дисплей не имеет внешнего контакта сброса (у большинства подобных дисплеев).
Далее в функции setup() мы инициализируем последовательную связь для целей отладки, проверяем доступен ли наш дисплей (он у нас подключен по интерфейсу I2C) или нет. Также мы устанавливаем адрес I2C. Далее мы очищаем экран дисплея с помощью метода clearDisplay(). Дополнительно мы поворачиваем экран дисплея с помощью метода setRotation. Затем мы делаем задержку 100 мс чтобы изменения вступили в силу.
1 2 3 4 5 6 7 8 9 10 11 |
void setup() { Serial.begin(115200); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); } display.clearDisplay(); display.setRotation(2); display.setTextSize(1); delay(100); } |
Функция return_voltage_value() используется для измерения значения напряжения с выхода АЦП, в качестве аргумента в нее передается номер контакта, на котором производится измерение. В этой функции мы объявим ряд необходимых нам переменных: tmp, ADCVoltage, inputVoltage и avg. Переменная tmp используется для хранения временного значения с выхода АЦП, которое мы получаем с помощью функции analogRead(). Эти значения мы в цикле считываем 150 раз, затем усредняем их и сохраняем в переменной avg. Затем мы рассчитываем значение напряжения на входе АЦП по известной формуле и сохраняем его в переменной ADCVoltage. Значение +0.138, которое вы видите в этой формуле, является калибровочным, вы можете изменить (откалибровать) его по своему желанию, используя мультиметр. Далее мы пересчитываем уровень напряжения с учетом делителя напряжения, с выхода которого напряжение подается на контакт модуля ESP32.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
double return_voltage_value(int pin_no) { double tmp = 0; double ADCVoltage = 0; double inputVoltage = 0; double avg = 0; for (int i = 0; i < 150; i++) { tmp = tmp + analogRead(pin_no); } avg = tmp / 150; ADCVoltage = ((avg * 3.3) / (4095)) + 0.138; inputVoltage = ADCVoltage / (R2_VOLTAGE / (R1_VOLTAGE + R2_VOLTAGE)); // formula for calculating voltage in i.e. GND return inputVoltage; } |
Также мы запрограммируем функцию return_current_value(), в качестве аргумента в нее передается номер контакта. Функция во многом аналогична ранее рассмотренной функции return_voltage_value(), но в ней вместо значения напряжения рассчитывается значение тока исходя из значения на выходе АЦП.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
double return_current_value(int pin_no) { double tmp = 0; double avg = 0; double ADCVoltage = 0; double Amps = 0; for (int z = 0; z < 150; z++) { tmp = tmp + analogRead(pin_no); } avg = tmp / 150; ADCVoltage = ((avg / 4095.0) * 3300); // Gets you mV Amps = ((ADCVoltage - ACSoffset) / mVperAmp); return Amps; } |
В функции void loop() мы объявим 4 переменных типа float. В них мы будем записывать модули (abs) значения тока и напряжения с входа и выхода устройства, поскольку с датчика ACS712 мы получаем отрицательные значения. Далее мы выводим эти значения в окно монитора последовательной связи с помощью функции Serial.print и на экран OLED дисплея с помощью функции display.print.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void loop() { float input_voltage = abs(return_voltage_value(INPUT_VOLTAGE_SENSE_PIN)) ; float input_current = abs(return_current_value(INPUT_CURRENT_SENSE_PIN)) ; float output_voltage = abs(return_voltage_value(OUTPUT_VOLTAGE_SENSE_PIN)) ; float output_current = abs((return_current_value(OUTPUT_CURRENT_SENSE_PIN))) ; input_current = input_current - 0.025; Serial.print("Input Voltage: "); Serial.print(input_voltage); Serial.print(" | Input Current: "); Serial.print(input_current); Serial.print(" | Output Voltage: "); Serial.print(output_voltage); Serial.print(" | Output Current: "); Serial.println(output_current); delay(300); display.clearDisplay(); display.setCursor(0, 0); display.print("I/P V: "); display.setCursor(37, 0); display.print(input_voltage); display.setCursor(70, 0); display.print("V"); } |
Тестирование работы ваттметра
Внешний вид собранной конструкции для тестирования нашего ваттметра показан на следующем рисунке. Автор проекта использовал для этой цели трансформатор на 30V и конвертер LM2596. В качестве нагрузки он использовал три резистора по 10 Ом, соединенных параллельно.
Также для проверки входного и выходного напряжения он подключил мультиметры. На выходе трансформатора напряжение составляет 32V, а на выходе конвертера – 3.95V.
На следующем рисунке показано значение тока, измеряемое мультиметром и нашим ваттметром. Как вы видите, мультиметр показывает значение тока 0,97 А, но если вы присмотритесь повнимательнее, то значение тока должно быть равно 1,0 А, это небольшое расхождение обусловлено нелинейностью, присутствующей в датчике ACS712.
Более подробно тестирование работы нашего ваттметра вы можете посмотреть на видео, приведенном в конце статьи.
Возможные улучшения проекта
В демонстрационных целях ваттметр собран на самодельной печатной плате, но его можно собрать и на хорошей (фабричной) печатной плате – в этом случае ее размеры можно значительно уменьшить, особенно если в качестве дискретных элементов использовать дешевые SMD компоненты.
Также представленная схема ваттметра не содержит никаких схем защиты. Использование схем защиты повысит безопасность использования рассмотренного нами ваттметра.
Повысить точность и стабильность измерений нашего ваттметра можно с помощью использования внешнего модуля АЦП (например, ADS1115), поскольку точность встроенного в модуль ESP32 АЦП не вызывает "чувства восторга".
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
/********* Arduino code for ESP32 based power meter to measure effeciancy www.circuitdigest.com *********/ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <math.h> #define SCREEN_WIDTH 128 // ширина OLED дисплея, в пикселах #define SCREEN_HEIGHT 64 // высота OLED дисплея, в пикселах #define INPUT_VOLTAGE_SENSE_PIN 33 #define OUTPUT_VOLTAGE_SENSE_PIN 35 #define INPUT_CURRENT_SENSE_PIN 32 #define OUTPUT_CURRENT_SENSE_PIN 34 float R1_VOLTAGE = 68000; //68K float R2_VOLTAGE = 6800; // 6.8K #define R1_Current 10000 //10K #define R2_Current 22000 //22K //We have used 50 as mVperp value; please refer to the documentation for further information double mVperAmp = 50; // используйте 100 для модуля 20A и 66 для модуля 30A double ACSoffset = /*1130*/ 1136; // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); void setup() { Serial.begin(115200); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); } delay(2000); display.clearDisplay(); display.setRotation(2); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 10); // Display static text display.println("Hello, world!"); display.display(); delay(100); } void loop() { // раскомментируйте для целей отладки //double a,b,c; // a = analogRead(INPUT_CURRENT_SENSE_PIN); // Serial.print("a: "); // Serial.println(a); // b = (a / 4095) * 3300; // Serial.print("b: "); // Serial.println(b); // c = ((b - ACSoffset) / mVperAmp) ; // Serial.print("c: "); // Serial.println(c); // Serial.print("Final: "); // Serial.println(return_current_value(INPUT_CURRENT_SENSE_PIN)); // delay(700); float input_voltage = abs(return_voltage_value(INPUT_VOLTAGE_SENSE_PIN)) ; float input_current = abs(return_current_value(INPUT_CURRENT_SENSE_PIN)) ; float output_voltage = abs(return_voltage_value(OUTPUT_VOLTAGE_SENSE_PIN)) ; float output_current = abs((return_current_value(OUTPUT_CURRENT_SENSE_PIN))) ; input_current = input_current - 0.025; Serial.print("Input Voltage: "); Serial.print(input_voltage); Serial.print(" | Input Current: "); Serial.print(input_current); Serial.print(" | Output Voltage: "); Serial.print(output_voltage); Serial.print(" | Output Current: "); Serial.println(output_current); delay(300); display.clearDisplay(); display.setCursor(0, 0); display.print("I/P V: "); display.setCursor(37, 0); display.print(input_voltage); display.setCursor(70, 0); display.print("V"); display.setCursor(0, 10); display.print("O/P V: "); display.setCursor(37, 10); display.print(output_voltage); display.setCursor(70, 10); display.print("V"); display.setCursor(0, 20); display.print("I/P I: "); display.setCursor(37, 20); display.print(abs(input_current)); display.setCursor(70, 20); display.print("A"); display.setCursor(0, 30); display.print("O/P I: "); display.setCursor(37, 30); display.print(abs(output_current)); display.setCursor(70, 30); display.print("A"); display.setCursor(0, 40); display.print("I/P P: "); display.setCursor(37, 40); display.print(abs(input_current * input_voltage)); display.setCursor(70, 40); display.print("W"); display.setCursor(0, 50); display.print("O/P P: "); display.setCursor(37, 50); display.print(abs(output_current * output_voltage)); display.setCursor(70, 50); display.print("W"); display.setTextSize(2); display.setCursor(85, 0); display.print("Eff"); display.setCursor(87, 23); display.print((abs((output_current * output_voltage) / (input_current * input_voltage)) * 100), 0); display.setCursor(92, 45); display.print("%"); display.setTextSize(1); display.display(); display.clearDisplay(); } double return_voltage_value(int pin_no) { double tmp = 0; double ADCVoltage = 0; double inputVoltage = 0; double avg = 0; for (int i = 0; i < 150; i++) { tmp = tmp + analogRead(pin_no); } avg = tmp / 150; ADCVoltage = ((avg * 3.3) / (4095)) + 0.138; inputVoltage = ADCVoltage / (R2_VOLTAGE / (R1_VOLTAGE + R2_VOLTAGE)); // formula for calculating voltage in i.e. GND return inputVoltage; } double return_current_value(int pin_no) { double tmp = 0; double avg = 0; double ADCVoltage = 0; double Amps = 0; for (int z = 0; z < 150; z++) { tmp = tmp + analogRead(pin_no); } avg = tmp / 150; ADCVoltage = ((avg / 4095.0) * 3300); // Gets you mV Amps = ((ADCVoltage - ACSoffset) / mVperAmp); return Amps; } |
Очень привлекательная тема и вполне доступное описание.
Однако, по моему скромному мнению, с дисплеем TFT(например ILI9341) и с размером, хотя бы 2,4", информацию было бы легче воспринимать. И ежели бы мне знания и умения в программировании позволяли, я бы так и поступил.
Спасибо что оценили мой труд и труд автора оригинала статьи. Насчёт удобства отображения информации предпочтения у всех разные. Ну если вы найдете в сети статью как подключать ваш дисплей к модулю ESP32, то совместить ее с данным проектом,я думаю, будет не сложно
Здравия! Подключение этого TFT к ESP32 затруднений не вызывает. Но, вот совместить с данной программой я не смогу - весьма слаб в программировании. К примеру, у меня есть дисплей SH1106. Вот с ним, тупым способом замены, всё получается. А вот переход с I2C на SPI для меня уже тёмный лес.
Я "железячник", но не программист.