用ESP8266+Blynk做一个电能表

发布于 2020-08-24  196 次阅读


最初来源:https://youtu.be/lrugreN2K4w

( )( )这个项目适合测试一些普通设备,虽然可以测手机,但并不建议对手机充电测量,现在市面上的手机需要的充电协议太多了,而且我也不会,不知道怎么整合进去.这里先把方法和结果共享出来,有兴趣的话大家可以试试把充电协议整合进去.
本文例子做的比较适合测量一些比较非主流的负责检查,例如12.6v锂电池充电,笔记本能耗等这些5.5DC插头插座的设备测量,总的来说适合远程或者短暂测量与观测使用.
其中来源视频是可以通过TF卡记录数据的,原品只是个单机版本,也不计算和显示电池容量,这里我使用了BLYNK,所以我是放弃了这个原有TF卡存储数据记录的功能的相关代码段,并加上了电池容量计算和显示,需要的可以点击上面的来源连接.


(点击图片放大)
通过接入blynk后使用手机客户端查看信息的版本,当时的手机是iPhone 6s 电池容量大概是1350 mAh,当时实际充满电后,模块测出大概差不多的容量,精度还算是可以的


(点击图片放大)
这是做成单机版即仅可以现场观察的用量,输入电压已经做成恒流充电的模块,专门用量测试电池容量

关于为啥不发交流版本的教程原因

  1. 220V带电测试具有危险性
  2. 新手容易接错线导致意外
  3. 个人感觉用途不大

具体看下图


(点击图片放大)
图1是在电脑上通过串口测试并观察交流负载电压等信息的图,图3是意外碰线被晒坏的模块

实现功能

  • 实时显示电流/电压/功率/已用电量/电池容量(接入blynk后可以异地远程查看)
  • 通过OLED屏实施显示(可选,并和远程客户端实时同步显示,OLED的数据延时少一点)
  • 数值精确到5位小数
  • 可以通过电子邮件地址发送记录数据到手机(需要谷歌邮箱)

演示视频(待审核,占位)

软件环境

  • Arduino库(点击进入下载或者在IDE内直接搜索相关关键字下载):
    1. Blynk(站内搜索Blynk有教程,或者直接去官网看文档)
    2. Adafruit INA219
    3. Adafruit SSD1306

材料

名称 数量 单位 备注
ESP8266 1 最好使用NodeMCU,本文单机版用的是Arduino nano
INA219电流检测模块 1 先看官方资料:https://www.adafruit.com/product/904
OLED模块 1 0.96寸,SSD1306主控,可以用SPI接口,这里用的是IIC接口
负载用电源 1 0-26V,0-3.2A
单片机用电源 1 3.3-5v,专门供单片机和模块使用
测试用负载 1 新手别用手机什么的,烧了你就惨了,先找个5V LED灯板或者风扇当做负载,熟悉了再用手机什么的测试,注意安全,免得烧了你的设备
杜邦线 若干
导线 若干 测试用的0.3平方左右就行,其他功率需要的请看下面补充资料

另外,建议手头有一个现成的电量表,以用作参考调整

材料的补充事项

  • 不熟练的别用手机之类贵的负载做测试
  • 事前记得现在清楚INA219的相关参数,不然过压什么烧东西就不好了
  • 关于电源输入/负载的导线选择必须选足够粗的,以免发生烧线之类情况,又或者出现降压之类情况,导线过流的话可是会引起发热甚至起火的哦,详细参考下面公式:

    再三强调:操作必须规范!

接线

声明:事前请先确定你自己会使用万用表的电流档测电流再继续下一步,因为INA219上的四个VIN+和VIN-的端口并不是表示输入,而是表示电流的方向,VIN+表示为:电流的输入端,VIN-表示电流的输出端,即VIN+接入负载电源端的VCC,然后VIN-接到负载端的VIN(正极输入).

  • 先接电流模块和负载部分
ESP8266 INA219 单片机用电源 (3.3-5V) 负载用电源 负载
SDA SDA
SCL SCL
VCC VCC VCC
GND GND GND GND GND
VIN+ VIN+
VIN- VIN-
  • 单片机和OLED部分
ESP8266 OLED 单片机用电源 (3.3-5V)
SDA SDA
SCL SCL
VCC VCC VCC
GND GND GND

代码

这里都是以英文显示的,我就懒得做字库了,没那个精力,有兴趣的朋友可以自己做做字库用中文显示.

单机版

#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_INA219 ina219;

unsigned long previousMillis = 0;
unsigned long interval = 100;
const int chipSelect = 10;
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float energy = 0;
float mAh = 0;


void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ina219.begin();
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    ina219values();
  }
  displaydata();
}

void displaydata() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println(loadvoltage, 3);
  display.setCursor(35, 0);
  display.println("V");

  display.setCursor(0, 10);
  display.println(current_mA, 1);
  display.setCursor(45, 10);
  display.println("mA");

  display.setCursor(63, 10);
  display.println("=");

  display.setCursor(75, 10);
  display.println(current_mA / 1000, 4);
  display.setCursor(120, 10);
  display.println("A");

  display.setCursor(63, 20);
  display.println("=");

  display.setCursor(0, 20);
  display.println(loadvoltage * current_mA, 1);
  display.setCursor(45, 20);
  display.println("mW");

  display.setCursor(75, 20);
  display.println(loadvoltage * current_mA / 1000, 4);
  display.setCursor(120, 20);
  display.println("W");

  display.setCursor(0, 30);
  display.println(energy, 5);
  display.setCursor(110, 30);
  display.println("mWh");

  display.setCursor(0, 40);
  display.println(mAh, 5);
  display.setCursor(110, 40);
  display.println("mAh");

  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(5, 50);
  display.println("PowerMeter");
  display.display();
}

void ina219values() {
  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  loadvoltage = busvoltage + (shuntvoltage / 1000);
  energy = energy + loadvoltage * current_mA / 3600;
  mAh = energy  / loadvoltage /10;
}

Blynk版

#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include<SPI.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_SSD1306.h>

char auth[] = "你的设备 KEY ";
char ssid[] = "你的 WIFI SSID";
char pass[] = "你的 WIFI 密码";

#define OLED_RESET LED_BUILTIN  //4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

Adafruit_INA219 ina219;
BlynkTimer timer;

unsigned long previousMillis = 0;
unsigned long interval = 100;
const int chipSelect = D8;
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float energy = 0;
float mAh = 0;


void sendSensor() {
  float mW = loadvoltage * current_mA;

  Blynk.virtualWrite(V1, loadvoltage);//负载电压
  Blynk.virtualWrite(V2, current_mA);//电流
  Blynk.virtualWrite(V3, mW);//功率
  Blynk.virtualWrite(V4, energy);//电量
  Blynk.virtualWrite(V5, mAh);//电池容量
}

void setup() {
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ina219.begin();
  Blynk.begin(auth, ssid, pass);
  timer.setInterval(1000L, sendSensor);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    ina219values();
    }
    displaydata();
  Blynk.run();
  timer.run();
}

//OLED显示的自定义函数
void displaydata() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println(loadvoltage, 3);
  display.setCursor(35, 0);
  display.println("V");

  display.setCursor(0, 10);
  display.println(current_mA, 1);
  display.setCursor(45, 10);
  display.println("mA");

  display.setCursor(63, 10);
  display.println("=");

  display.setCursor(75, 10);
  display.println(current_mA / 1000, 4);
  display.setCursor(120, 10);
  display.println("A");

  display.setCursor(63, 20);
  display.println("=");

  display.setCursor(0, 20);
  display.println(loadvoltage * current_mA, 1);
  display.setCursor(45, 20);
  display.println("mW");

  display.setCursor(75, 20);
  display.println(loadvoltage * current_mA / 1000, 4);
  display.setCursor(120, 20);
  display.println("W");

  display.setCursor(0, 30);
  display.println(energy, 5);
  display.setCursor(110, 30);
  display.println("mWh");

  display.setCursor(0, 40);
  display.println(mAh, 5);
  display.setCursor(110, 40);
  display.println("mAh");

//显示一个大大的LOGO
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(5, 50);
  display.println("PowerMeter");
  display.display();
}

void ina219values() {
  shuntvoltage = ina219.getShuntVoltage_mV();        //分流电压
  busvoltage = ina219.getBusVoltage_V();             //母线电压
  current_mA = ina219.getCurrent_mA();               //电流,单位为毫安
  loadvoltage = busvoltage + (shuntvoltage / 1000);  //负载电压
  energy = energy + loadvoltage * current_mA / 3600; //电能
  mAh = energy  / loadvoltage / 10;                  //毫安时
}

补充

  • 如果发现数据不太对,请用万用表检查线的降压情况,导线不用太细.
  • 导线不建议太长太细,尽量用铜线,不然你要花点时间去把电压调准确.
  • 至于曲线图功能,可以开启Blynk APP里头的super chart,服务器端设置好邮箱的话可以把数据发到你邮箱,只是官方文档写的是要求必须谷歌邮箱.
  • 如果是Blynk版,那么手机端收到的数值对比OLED端的数值来说,会稍有延迟.
  • INA219最大支持监测的电压为直流0-26V,这一点要注意.
  • 源代码作者来源里头用的是NANO,可是我亲测是会因为单片机内存空间不足而导致无法编译的,后来改用mega可以.要是你喜欢的话可以直接用ESP8266一个带TF卡记录的单机版 :huaji: .
  • 如果你打算做成可以随时重置WIFI的功能,可以参考我之前做的小时钟的这编文章,可以自己试试在代码里头整合一下,这里我就不再啰嗦了.

群体只会干两种事:锦上添花或落井下石——《乌合之众》