【搬运】使用 M5Stack 测量 IoT 设备的电流消耗
本帖最后由 vany5921 于 2020-4-3 11:26 编辑使用 M5Stack 测量 IoT 设备的电流消耗为了使 IoT 设备在电池和太阳能电池上长时间运行,IoT 设备必须低功耗。 为了了解 IoT 设备的功耗,我们制作了一个电流监视器,以 0.1mA 为单位测量 IoT 设备的电流消耗。降低 IoT 设备的功耗IoT 器件的电流消耗因控制微机的处理而异。 无线通信,特别是Wi-Fi和蓝牙,消耗较大的电流,有些传感器消耗较大的电流。 因此,由于传感器访问和通信编程,IoT 设备的功耗会有所不同。为了使 IoT 设备功耗更低,您不仅需要了解某一时刻的电流消耗,还需要了解并改进某一部分的电流消耗累积值。 例如,如果设备在 5 分钟的周期内测量数据并将其发送到云服务,则需要了解并改进 5 分钟的总电流消耗。 因此,电流监视器的间隔的总电流消耗是可以测量的。电流计模块 INA226PRC使用名为 INA226PRC 的草莓 Linux模块测量电流值。
该模块在电源线中放置 25 mΩ 的电阻(分流电阻),并通过使用 INA226 电流传感器测量两端的电压,以测量流经电源线的电流。 电流测量的分辨率为 0.1mA,电压分辨率为 1.25mV。INA226 通过 I2C 与微控制器通信。 M5Stack 连接如下:
在原理图的 ISENSE+ 和 GND 之间连接要测量的电池和发电设备,并在 ISENSE 和 GND 之间连接要测量的微控制器。 为了帮助您实现 M5Stack 信号线,我们使用扩展板"M5Stack 的 SideBB",使 M5Stack信号线更易于面包板处理。 由于 M5Stack 的扩展模块可以使用面包板,因此这种测量仪器可以紧凑地组合在一起,非常方便。
电流监视器软件相关文件地址https://github.com/AmbientDataInc/M5Stack_CurrentMonitor/tree/master/INA226PRC要测量一个间隔的总电流消耗,请定期测量电流并将其记录在内存中。 测量周期为 2 毫秒 (ms)、4 毫秒、10ms、20ms 和50ms。 由于内存包含 3,000 个数据,因此测量间隔为 6 秒(2ms x 3,000)到 150 秒(50ms x 3,000)。使用 ESP32 Arduino 计时器功能实现周期性处理,如下所示: 确保每 sampling 毫秒调用计时器中断处理函数 onTimer0。 在周期处理中,将变量 t0flag 设置为 0 并等待计时器中断,在中断处理函数中通过将 t0flag 设置为 1 来取消等待,以便循环处理每毫秒进行一次。
volatile int t0flag; // 中断标志
void IRAM_ATTR onTimer0() { // 中断处理函数
t0flag = 1;
}
samplingTimer = timerBegin(TIMER0, 80, true); // 初期设定1微秒的计时器
timerAttachInterrupt(samplingTimer, &onTimer0, true); //设置中断处理函数
timerAlarmWrite(samplingTimer, sampling * 1000, true); // sampling设置毫秒定时器值
timerAlarmEnable(samplingTimer); // 启动计时器
while (true) { //
t0flag = 0;
while (t0flag == 0) { // 等待计时器中断
delay(0);
}
//
}
timerAlarmDisable(samplingTimer); //停止计时器
在循环处理体中,电流传感器模块 INA226PRC 读取电流和电压值,并记录在内存中。 确定电流阈值,确定要测量的 IoT 设备在超过阈值后开始录制。bool started = false; // 测定开始标志
int indx = 0; // 要记录的数据索引
while (true) {
t0flag = 0;
while (t0flag == 0) { // 等待计时器中断
delay(0);
}
short amp = ina226prc.readCurrentReg(); // 从INA 226 PRC读取电流值
short volt = ina226prc.readVoltageReg(); // 从INA 226 PRC读取电压值
if (!started) {
// 如果电流值小于阈值(startthreshold),则不开始测量
if (amp * 0.1 > -(float)startthreshold && amp * 0.1 < (float)startthreshold) {
continue;
}
started = true; // 电流值超过阈值后开始测量
}
ampbuf = amp; // 把电流值记录在存储器里。
voltbuf = volt; // 把电压值记录在存储器里。
if (++indx >= NSAMPLES) { //如果数据数超过样本数,则结束周期处理
break;
}
}
测量周期和测量开始阈值允许使用 M5Stack LCD 和按钮进行选择。 记录的数据作为 csv 文件写入 SD 卡,并在 LCD 上显示。
// 用INA 226 PRC测量每4毫秒3000次、12秒的电流值,并写在SD卡上
#include <M5Stack.h>
#include <Wire.h>
#include "INA226PRC.h"
#include "menu.h"
INA226PRC ina226prc;
void beep(int freq, int duration, uint8_t volume);
#define TIMER0 0
hw_timer_t * samplingTimer = NULL;
#define NSAMPLES 3000 // 4ms x 3000 = 12秒
short ampbuf;
short voltbuf;
int sampling= 4;// 采样间隔(毫秒)
int startthreshold = 3;// 开始记录的电流值(毫安)
Menu menu;
volatile int t0flag;
void IRAM_ATTR onTimer0() {
t0flag = 1;
}
int selectitem(int *candi, int items, int val, char *tail) {
int focused;
for (int i = 0; i < items; i++) {
if (candi == val) {
focused = i;
}
}
M5.Lcd.fillScreen(BLACK);
menu.setMenu("up", "OK", "down");
bool first = true;
while (true) {
bool modified = false;
M5.update();
if (M5.BtnA.wasPressed()) {
focused--;
modified = true;
}
if (M5.BtnC.wasPressed()) {
focused++;
modified = true;
}
if (M5.BtnB.wasPressed()) {
M5.Lcd.fillScreen(BLACK);
return candi;
}
if (first || modified) {
first = false;
beep(1000, 100, 2);
for (int i = 0; i < items; i++) {
M5.Lcd.setCursor(100, 40 + i * 20);
int16_t textcolor = ((focused % items) == i) ? BLACK : WHITE;
int16_t backcolor = ((focused % items) == i) ? WHITE : BLACK;
M5.Lcd.setTextColor(textcolor, backcolor);
M5.Lcd.printf(" %d ", candi);
M5.Lcd.setTextColor(WHITE, BLACK);
M5.Lcd.print(tail);
}
}
}
}
void config() {
int focused = 2;
const int nItems = 3;
int thresholds[] = {2, 3, 5, 10, 20};
int samplings[] = {2, 4, 10, 20, 50};
bool first = true;
while (true) {
bool modified = false;
M5.update();
if (M5.BtnA.wasPressed()) {
focused--;// upボタン
modified = true;
}
if (M5.BtnC.wasPressed()) {
focused++;// downボタン
modified = true;
}
if (M5.BtnB.wasPressed()) {// changeボタン
modified = true;
switch (focused % nItems) {
case 0:
startthreshold = selectitem(thresholds, sizeof(thresholds) / sizeof(int), startthreshold, "mA");
break;
case 1:
sampling = selectitem(samplings, sizeof(samplings) / sizeof(int), sampling, "ms");
break;
case 2:
default:
return;
}
}
if (first || modified) {//
first = false;
beep(1000, 100, 2);
menu.setMenu("up", "GO", "down");
M5.Lcd.setCursor(20, 40);
M5.Lcd.print("Start threshold: ");
if ((focused % nItems) == 0) M5.Lcd.setTextColor(BLACK, WHITE);
M5.Lcd.printf(" %d ", startthreshold);
if ((focused % nItems) == 0) M5.Lcd.setTextColor(WHITE, BLACK);
M5.Lcd.print("mA");
M5.Lcd.setCursor(20, 100);
M5.Lcd.print("Sampling period: ");
if ((focused % nItems) == 1) M5.Lcd.setTextColor(BLACK, WHITE);
M5.Lcd.printf(" %d ", sampling);
if ((focused % nItems) == 1) M5.Lcd.setTextColor(WHITE, BLACK);
M5.Lcd.print("ms");
M5.Lcd.setCursor(20, 160);
if ((focused % nItems) == 2) M5.Lcd.setTextColor(BLACK, WHITE);
M5.Lcd.print(" DONE ");
if ((focused % nItems) == 2) M5.Lcd.setTextColor(WHITE, BLACK);
}
}
}
#define X0 10
#define Y0 220
void drawData(short maxamp) {
M5.Lcd.fillRect(0, 0, 320, 220, BLACK);
maxamp = ((maxamp / 100) + 1) * 100;
for (int i = 0; i < 299; i++) {
int y0 = map(ampbuf, 0, maxamp, Y0, 0);
int y1 = map(ampbuf[(i + 1) * 10], 0, maxamp, Y0, 0);
M5.Lcd.drawLine(i + X0, y0, i + 1 + X0, y1, WHITE);
Serial.printf("%d, %d,%d, %d\r\n", ampbuf, ampbuf[(i + 1) * 10], y0, y1);
}
M5.Lcd.drawLine(X0, Y0, 310, Y0, WHITE);
M5.Lcd.drawLine(X0, 0, X0, Y0, WHITE);
}
void setup() {
M5.begin();
Wire.begin();
ina226prc.begin();
Serial.print("Manufacture ID: ");
Serial.println(ina226prc.readId(), HEX);
M5.Lcd.setTextSize(2);
config();
M5.Lcd.fillScreen(BLACK);
beep(1000, 100, 2);
menu.setMenu("start", "", "");
M5.Lcd.setCursor(20, 100);
M5.Lcd.print("Press A button");
M5.Lcd.setCursor(40, 120);
M5.Lcd.print("to start sampling");
while (true) {
M5.update();
if (M5.BtnA.wasPressed()) break;
}
M5.Lcd.fillScreen(BLACK);
beep(2000, 100, 2);
samplingTimer = timerBegin(TIMER0, 80, true);//
timerAttachInterrupt(samplingTimer, &onTimer0, true);//
timerAlarmWrite(samplingTimer, sampling * 1000, true);//
timerAlarmEnable(samplingTimer);//
bool started = false;
int indx = 0;
short maxamp = 0;
M5.Lcd.fillRect(50, 100, 200, 10, BLACK);
while (true) {
t0flag = 0;
while (t0flag == 0) {//
delay(0);
}
short amp = ina226prc.readCurrentReg();
short volt = ina226prc.readVoltageReg();
if (!started) {
// 如果电流值小于阈值(startthreshold),则不开始测量
if (amp * 0.1 > -(float)startthreshold && amp * 0.1 < (float)startthreshold) {
continue;
}
started = true;//
}
ampbuf = amp;//
voltbuf = volt;//
maxamp = max(amp, maxamp);
M5.Lcd.setCursor(100, 100);
M5.Lcd.print(indx * 100 / NSAMPLES); M5.Lcd.print(" %");
if (++indx >= NSAMPLES) {//
break;
}
}
timerAlarmDisable(samplingTimer);//
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(20, 100);
beep(2000, 400, 2);
char fname;
sprintf(fname, "/curLog.csv");
for (int i = 0; SD.exists(fname); i++) {
sprintf(fname, "/curLog(%d).csv", i + 1);
}
File f = SD.open(fname, FILE_WRITE);
if (f) {
f.println("time, current(mA), volt(mV)");
for (int i = 0; i < NSAMPLES; i++) {
f.printf("%d, %.2f, %.2f\r\n", sampling * i, ampbuf * 0.1, voltbuf * 1.25);
}
f.close();
M5.Lcd.print("Data written to");
M5.Lcd.setCursor(40, 120);
M5.Lcd.print(fname);
} else {
M5.Lcd.printf("open error %s", fname);
}
menu.setMenu("view", "", "");
M5.Lcd.setCursor(40, 160);
M5.Lcd.print("Press A button");
M5.Lcd.setCursor(40, 180);
M5.Lcd.print("to view data");
while (true) {
M5.update();
if (M5.BtnA.wasPressed()) {
drawData(maxamp);
}
}
}
void loop()
{
}
页:
[1]