Все мы знакомы со счетчиками электроэнергии, которые установлены сейчас в каждой квартире или домохозяйстве. Обычно мы смотрим их показания один раз в месяц когда заполняем квитанции на оплату коммунальных услуг. Но иногда количество потребленной электроэнергии становится для нас неожиданностью – мы обнаруживаем что потребили ее очень много. Поэтому в данной статье мы рассмотрим проект умного измерителя электроэнергии на ESP12 и Arduino, который будет информировать нас по SMS/E-mail когда количество потребленной энергии будет достигать определенного значения (границы). Данный проект относится к так называемой концепции интернета вещей (IoT – Internet of Things).
Для передачи информации по WiFi мы будем использовать модуль ESP8266, а контроль потребления электроэнергии будем осуществлять с помощью приложения для Android.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- ESP12/NodeMCU (устройство управления многосторонней связью) (купить на AliExpress).
- ACS712-30Amp Current sensor (датчик тока) (купить на AliExpress).
- Любой потребитель электроэнергии переменного тока.
- Male-Female Wires (соединительные провода папа-мама).
Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158
Принцип работы датчика тока ACS712
Прежде чем приступить к рассмотрению проекта остановимся кратко на принципах работы датчика тока ACS712 поскольку он является ключевым элементом нашего проекта. Измерение силы тока, а особенно силы переменного тока, всегда является достаточно сложной задачей вследствие наличия большого количества шумов, вызванных проблемами с изоляцией и т.д. Но с использованием датчика тока ACS712 эта задача значительно упрощается.
Этот датчик построен на использовании эффекта Холла, открытым ученым Эдвином Холлом. В соответствии с данным эффектом когда проводник с током помещается в магнитное поле на его концах формируется напряжение, перпендикулярное направлению протекания тока и направлению действующего магнитного поля. Измерять это напряжение мы будем в милливольтах и будем называть его напряжением Холла. Величина этого напряжения будет пропорциональна величине протекающего через проводник тока.
Основным достоинством датчика тока ACS712 является то, что он может измерять как переменный (AC), так и постоянный ток (DC) и он также обеспечивает изоляцию между нагрузкой и измерительным устройством (в нашем случае это будет плата Arduino. Как показано на следующем рисунке, датчик тока ACS712 имеет три контакта – Vcc (питающее напряжение), Vout (выход) и Ground (земля).
Слева на рисунке показаны два контакта, которые подсоединяются к тому месту, где необходимо измерить ток. Датчик работает от напряжения +5V – его необходимо подать на контакт Vcc датчика. Контакт Ground датчика необходимо подсоединить к земле схемы. Если сила измеряемого тока равна нулю, то на выходном контакте датчика напряжение равно 2500mV, если протекающий ток положителен, то напряжение на выходе датчика будет больше 2500mV, если отрицателен – то меньше 2500mV.
Для считывания напряжения с этого контакта мы будем использовать один из аналоговых входов Arduino – на выходе его АЦП (аналогово-цифровой преобразователь) будет значение 512 когда на входе контакта будет напряжение 2500mV – то есть когда ток не протекает. Это значение будет уменьшаться когда ток будет протекать в обратном (отрицательном) направлении, и увеличиваться когда ток будет протекать в прямом (положительном) направлении. В следующей таблице представлены примеры значений на выходе АЦП аналогового контакта Arduino в зависимости от величины протекающего через датчик тока.
Эти значения были рассчитаны на основе даташита на датчик ACS712. Вы их также можете рассчитать по следующим формулам:
Vout Voltage(mV) = (ADC Value/ 1023)*5000
Ток через проводник (A) = (Vout(mv)-2500)/185
Работа схемы
Схема измерителя электроэнергии на основе ESP12 и Arduino представлена на следующем рисунке.
- подсоединить контакт Rx ESP12 к контакту Tx платы Arduino;
- подсоединить контакт Tx ESP12 к контакту Rx платы Arduino.
У NodeMCU (ESP12) нет аналоговых контактов, поэтому для связи с данным модулем мы использовали порт последовательной связи. Но данный модуль работает с напряжениями 3.3 Вольта, поэтому чтобы не повредить его напряжением 5 В с контактов Arduino мы использовали делитель напряжения.
Выходной контакт датчика тока в схеме подключен к аналоговому контакту A0 платы Arduino.
Внешний вид собранной схемы показан на следующем рисунке.
- Зарегистрировать себе аккаунт на AdaFruit для хранения и считывания данных потребления электроэнергии.
- Создать Applet (прикладную программу) в сервисе IFTTT для формирования сообщений при помощи SMS/Email.
- Написать коды программ для Arduino и ESP12 Wi-Fi модуля.
Более подробно все эти процессы описаны далее в статье.
Регистрация аккаунта в AdaFruit
Здесь вам необходимо выполнить следующие шаги:
Шаг 1. Зарегистрировать аккаунт на Adafruit IO или войти в свой аккаунт если вы там уже зарегистрированы.
Шаг 2. Кликните на My account -> Dashboard.
Шаг 3. Кликните на Actions и создайте новую приборную доску (Dashboard).
Шаг 4. Введите имя и название для вашего проекта и нажмите Create (создать).
Шаг 5. Нажмите на Key button (кнопка с изображением ключа – см. рисунок) и запишите ключи, которые предоставит вам этот сервис (см. рисунок). Далее эти ключи будут использоваться в коде программы.
Шаг 6. Кликните на кнопку ‘+’ чтобы создать новый блок и кликните на Gauge (масштаб) чтобы отобразить уровень расходования электроэнергии. Вы можете использовать простое текстовое поле для отображения этой информации.
Шаг 7. Далее введите имя фида (Name of Feed) и нажмите на Create (создать). Затем выберите фид и кликните на Next step (следующий шаг).
Шаг 8. В настройках блока (block settings) введите минимальное (в нашем случае 0) и максимальное значения (в нашем случае 100). В дальнейшем вы можете изменить эти введенные значения.
Шаг 9. Ваш вид для учета электроэнергии (Power feed) успешно создан. Теперь создайте фид для отображения счёта (Bill), нажав на кнопку “+”.
После этого вам необходимо будет установить соединение с AdaFruit IO чтобы передавать SMS/E-mail с использованием сервиса IFTTT.
Создание прикладной программы в IFTTT для передачи SMS/Email
Шаг 1. Зарегистрируйтесь в сервисе IFTTT или войдите туда если у вас уже есть там аккаунт.
Шаг 2. На вкладке My Applets (мои прикладные программы) кликните на New Applet (новая прикладная программа).
Шаг 3. Кликните на +this.
Шаг 4. Найдите AdaFruit и кликните на нее.
Шаг 5. Кликните на «Monitor a feed on AdaFruit IO» (мониторить фид в AdaFruit IO).
Шаг 6. Выберите счет (bill) в качестве фида (Feed), взаимоотношение (Relationship) выберите ‘equal to’ (равно) и введите границу (мы ввели 4) при достижении которой вам будет высылаться уведомление на E-mail. Кликните на Create action (создать действие).
Шаг 7. Кликните на +that. В поиске введите G-mail, кликните потом на ней и залогиньтесь со своими данными в g-mail.
Шаг 8. Кликните на send yourself an email (передать самому себе email).
Шаг 9. Запишите свой subject (тему) и ее описание (body) как показано на рисунке и кликните на create.
Шаг 10. Ваше уведомление создано. Посмотрите его и нажмите на finish (завершить).
С интеграцией нашего проекта в сеть интернет мы закончили, теперь можно переходить к написанию кода программы.
Объяснение кода программы
В нашем проекте мы используем последовательную связь между ESP12 и Arduino. Поэтому нам необходимо написать программу и для Arduino, и для ESP12 (NodeMCU).
Объяснение кода программы для передающей части (для Arduino Uno)
Полный код программы приведен в конце статьи, здесь же мы рассмотрим его наиболее важные фрагменты. В программе мы будем использовать специальную библиотеку для работы с датчиком тока, которую можно скачать по следующей ссылке. В этой библиотеке используются специальные функции для расчета силы тока. Конечно, эти функции вы можете запрограммировать и самостоятельно, но в данной библиотеке используются специальные алгоритмы для точного расчета силы тока, поэтому целесообразнее использовать все таки ее.
Первым делом в программе необходимо подключить данную библиотеку.
1 |
#include "ACS712.h" |
Создадим массив чтобы хранить в нем значения мощности, которые затем будут передаваться в NodeMCU.
1 |
char watt[5]; |
Сообщим Arduino Uno, что к ее контакту A0 будет подключен датчик тока ACS712-30Amp (на 30 Ампер). Измените соответствующим образом первый аргумент в следующей команде если вы будете использовать вариант датчика тока на 20 или 5 Ампер.
1 |
ACS712 sensor(ACS712_30A, A0); |
Далее, в функции setup установим скорость последовательной связи равную 115200 бод/с для обмена данными с NodeMCU. Вызовем функцию sensor.calibrate() для калибровки датчика тока (чтобы в дальнейшем считывать с него правильные значения).
1 2 3 4 |
void setup() { Serial.begin(115200); sensor.calibrate(); } |
В функции loop вызовем функцию sensor.getCurrentAC() чтобы считать текущее значение тока и сохранить его в переменной I. После получения значения тока рассчитаем мощность по стандартной формуле P=V*I. В качестве значения напряжения мы использовали 230V – измените это значение если в вашей сети другое значение напряжения.
1 2 3 4 |
void loop() { float V= 230; float I = sensor.getCurrentAC(); float P = V * I; |
Следующие три строчки кода конвертируют значение мощности в ватт-часы (Wh) – то есть значение мощности умножается на время работы.
1 2 3 |
last_time = current_time; current_time = millis(); Wh = Wh+ P *(( current_time -last_time) /3600000.0) ; |
Затем нам необходимо конвертировать эти ватт-часы в символьный массив чтобы их можно было передать на NodeMCU – для этой цели мы будем использовать функцию dtostrf().
1 |
dtostrf(Wh, 4, 2, watt); |
Формат этой функции выглядит следующим образом:
1 |
dtostrf(floatvar, StringLengthIncDecimalPoint, numVarsAfterDecimal, charbuf); |
Далее этот массив символьных данных передадим в буфер последовательной связи с помощью функции Serial.write() – то есть осуществим передачу значений ватт-часов в NodeMCU.
1 2 3 |
Serial.write(watt); delay(10000); } |
Объяснение кода программы для приемной части (для NodeMCU ESP12)
Для написания данной программы нам понадобится библиотека AdaFruit MQTT library, которую можно скачать по этой ссылке.
После этого откройте Arduino IDE. В ней откройте examples -> AdaFruit MQTT library -> mqtt_esp8266.
Нам необходимо будет изменить этот код в соответствии с ключами, полученными ранее в сервисе Adafruit IO (AIO keys), вашими настройками для Wi-Fi и поступающими по последовательному порту связи данными от платы Arduino. Первым делом в программе нам необходимо подключить библиотеки для работы с Wi-Fi модулем ESP12 и для работы с AdaFruit MQTT.
1 2 3 |
#include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" |
Инициализируем SSID и пароль (Password) для Wi-Fi, к которому мы будем подключать наш модуль ESp-12e.
1 2 |
#define WLAN_SSID "xxxxxxxx" #define WLAN_PASS "xxxxxxxxxxx" |
Далее инициализируем сервер AdaFruit и порт сервера на “io.adafruit.com” – для него зафиксировано значение «1883».
1 2 |
#define AIO_SERVER "io.adafruit.com" #define AIO_SERVERPORT 1883 |
В следующих командах вам необходимо заменить имя пользователя (username) и ключи с сервиса Adafruit IO (AIO keys) на ваши значения.
1 2 |
#define AIO_USERNAME "********" #define AIO_KEY "******************************" |
Затем мы создадим класс для ESP12 с именем WiFiClient чтобы с его помощью затем коннектиться к серверу MQTT.
1 |
WiFiClient client; |
Установка класса клиента для MQTT осуществляется с помощью команды следующего формата:
1 |
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); |
Необходимо установить фиды с именами ‘Power’ и ‘bill’ для публикации изменений в них.
1 2 |
Adafruit_MQTT_Publish Power = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Power"); Adafruit_MQTT_Publish bill = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/bill"); |
В функции setup мы будем подключать Wi-Fi модуль к точке доступа Wi-fi.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void setup() { Serial.begin(115200); delay(10); Serial.println(F("Adafruit MQTT demo")); // Connect to WiFi access point. Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(WLAN_SSID); WiFi.begin(WLAN_SSID, WLAN_PASS); …. …. … } |
В функции loop мы будем проверять поступающие данные от платы Arduino и публиковать эти данные в AdaFruit IO.
1 2 3 4 5 6 7 8 |
void loop() { //необходимо удостовериться в том, что соединение с сервером MQTT "живо" // то есть мы делаем первое соединение и автоматический реконнект при пропадании соединения //более подробно это можно посмотреть в функции MQTT_connect(); MQTT_connect(); int i=0; float watt1; |
В этой функции осуществляется проверка поступающих данных от Arduino и сохранение этих данных в массив watt[] с помощью использования функции serial.read().
1 2 3 4 5 6 7 |
if(Serial.available() > 0 ){ delay(100); //эта задержка необходима для того, чтобы все переданные по последовательному порту данные были приняты вместе while(Serial.available() && i<5) { watt[i++] = Serial.read(); } watt[i++]='\0'; } |
С помощью функции atof() осуществляется конвертирование принятых символов в значение вещественного типа, которое сохраняется в переменной watt1.
1 |
watt1 = atof(watt); |
Затем мы осуществляем расчет счета за потребленную электроэнергию при помощи умножения значения потребленной энергии (в ватт-часах) на значение тарифа за электричество. На 1000 в следующей команде мы делим для того, чтобы перевести ватт-часы в киловатт-часы (потому что обычно тариф за электроэнергию указывается для потребленных кВт/ч).
1 |
bill_amount = watt1 * (energyTariff/1000); // 1unit = 1kwH |
Затем мы можем начинать передачу в интернет полученных значений.
1 2 3 |
Serial.print(F("\nSending Power val ")); Serial.println(watt1); Serial.print("..."); |
В следующем фрагменте кода осуществляется публикация полученных значений мощности в соответствующем фиде.
1 2 3 4 5 |
if (! Power.publish(watt1)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } |
Далее осуществляется публикация счета за электричество.
1 2 3 4 5 |
if (! bill.publish(bill_amount)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } |
Наше значение счета за электричество может изменяться достаточно быстро (при включении и выключении новых потребителей электроэнергии), однако сервису IFTTT необходимо некоторое время для того чтобы исполнить написанный нами ранее applet (прикладную программу), поэтому в программе предусмотрена задержка чтобы не перегружать сервис IFTTT слишком частыми запросами. Вы можете изменить границу, при превышении которой вам будет высылаться email, также вы может изменить и другие настройки в IFTTT AdaFruit IO setup.
1 2 3 4 5 6 7 8 |
if (bill_amount==4){ for (int i =0; i<=2; i++) { bill.publish(bill_amount); delay(5000); } bill_amount =6; } |
Полный код программы для Arduino и NodeMCU ESP12 приведен в конце данной статьи.
Загрузите коды этих программ в эти устройства. Соедините все элементы проекта как показано в приведенной ранее схеме и откройте сайт io.adafruit.com. Откройте приборную доску (dashboard), которую вы там создали. После этого вы сможете наблюдать как в ней будут меняться значения потребления электроэнергии и счета за электричество.
Когда ваш счет достигнет значения 4 (вы можете изменить это значение на нужное вам) вы получите email примерно следующего вида:
Приложение для Android для контроля потребления электроэнергии
Вы можете использовать приложение для Android для контроля описанных значений потребления электроэнергии и счета за электричество. Для этого скачайте MQTT Dashboard android app из Play store.
Чтобы осуществить в нем соединение с io.adafruit.com выполните следующую последовательность шагов.
Шаг 1. Откройте приложение и кликните в нем на знак “+”. Напишите в поле Client Id любое значение которое захотите. Сервер и порт оставьте такими как показано на следующем рисунке.
Вы можете получить имя пользователя (Username) и пароль (Active key) с приборной доски AdaFruit IO как показано на следующем рисунке.
Шаг 2. Выберите Electricity Meter (измеритель электричества) и выберите Subscribe (подписаться). Для подписки укажите дружественное (сетевое) имя (friendly name) и тему (topic) в формате ‘yourusername’/feeds/’feedname’, затем нажмите create (создать).
Шаг 3. Аналогичным образом сделайте подписку на значение счета за электричество (bill feed).
Шаг 4. После того как ваши устройства начнут потреблять электроэнергию значения потребляемой мощности и счета за электричество будут отображаться в приложении.
Таким образом, мы сконструировали умный измеритель электроэнергии, благодаря которому мы можем контролировать потребление электроэнергии из любой точки земного шара (где есть интернет). Также на нашем сайте вы можете посмотреть и другие проекты, относящиеся к категории интернета вещей.
Исходный код программы
Код программы для Arduino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include "ACS712.h" char watt[5]; ACS712 sensor(ACS712_30A, A0); unsigned long last_time =0; unsigned long current_time =0; float Wh =0 ; void setup() { Serial.begin(115200); sensor.calibrate(); } void loop() { float V = 230; float I = sensor.getCurrentAC(); // Serial.println(I); float P = V * I; last_time = current_time; current_time = millis(); Wh = Wh+ P *(( current_time -last_time) /3600000.0) ; dtostrf(Wh, 4, 2, watt); Serial.write(watt); delay(10000); } |
Код программы для NodeMCU ESP12
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 |
#include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #define WLAN_SSID "a*************" #define WLAN_PASS "*******************" char watt[5]; #define AIO_SERVER "io.adafruit.com" #define AIO_SERVERPORT 1883 #define AIO_USERNAME "rjrishabh" #define AIO_KEY "***********************" WiFiClient client; int bill_amount = 0; unsigned int energyTariff = 8.0; Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); Adafruit_MQTT_Publish Power = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Power"); Adafruit_MQTT_Publish bill = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/bill"); void MQTT_connect(); void setup() { Serial.begin(115200); delay(10); Serial.println(F("Adafruit MQTT demo")); // соединение с точкой доступа WiFi Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(WLAN_SSID); WiFi.begin(WLAN_SSID, WLAN_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void loop() { // Ensure the connection to the MQTT server is alive (this will make the first // connection and automatically reconnect when disconnected). See the MQTT_connect // function definition further below. MQTT_connect(); int i=0; float watt1; if(Serial.available() > 0 ){ delay(100); //allows all serial sent to be received together while(Serial.available() && i<5) { watt[i++] = Serial.read(); } watt[i++]='\0'; } watt1 = atof(watt); bill_amount = watt1 * (energyTariff/1000); // 1unit = 1kwH Serial.print(F("\nSending Power val ")); Serial.println(watt1); Serial.print("..."); if (! Power.publish(watt1)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } if (! bill.publish(bill_amount)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } if (bill_amount==4){ for (int i =0; i<=2; i++) { bill.publish(bill_amount); delay(5000); } bill_amount =6; } delay(5000); } // функция для соединения и повторного соединения (при разрыве) с MQTT сервером // Should be called in the loop function and it will take care if connecting. void MQTT_connect() { int8_t ret; // стоп если уже подсоедине if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); } } Serial.println("MQTT Connected!"); } |