【搬运】使用 M5Stack 测量 IoT 设备的电流消耗-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1685|回复: 0

【搬运】使用 M5Stack 测量 IoT 设备的电流消耗

[复制链接]
发表于 2020-4-3 11:04 | 显示全部楼层 |阅读模式
本帖最后由 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模块测量电流值。
ina226prc-768x574.jpg

该模块在电源线中放置 25 mΩ 的电阻(分流电阻),并通过使用 INA226 电流传感器测量两端的电压,以测量流经电源线的电流。 电流测量的分辨率为 0.1mA,电压分辨率为 1.25mV。
INA226 通过 I2C 与微控制器通信。 M5Stack 连接如下:

schema.jpg

在原理图的 ISENSE+ 和 GND 之间连接要测量的电池和发电设备,并在 ISENSE 和 GND 之间连接要测量的微控制器。
为了帮助您实现 M5Stack 信号线,我们使用扩展板"M5Stack 的 SideBB",使 M5Stack信号线更易于面包板处理。 由于 M5Stack 的扩展模块可以使用面包板,因此这种测量仪器可以紧凑地组合在一起,非常方便。

M5_INA226PRC-1024x896.jpg

电流监视器软件
要测量一个间隔的总电流消耗,请定期测量电流并将其记录在内存中。 测量周期为 2 毫秒 (ms)、4 毫秒、10ms20ms 50ms。 由于内存包含 3000 个数据,因此测量间隔为 6 秒(2ms x 3000)到 150 秒(50ms x 3000)。
使用 ESP32 Arduino 计时器功能实现周期性处理,如下所示: 确保每 sampling 毫秒调用计时器中断处理函数 onTimer0。 在周期处理中,将变量 t0flag 设置为 0 并等待计时器中断,在中断处理函数中通过将 t0flag 设置为 1 来取消等待,以便循环处理每毫秒进行一次。

[mw_shl_code=arduino,true]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); //停止计时器
[/mw_shl_code]
在循环处理体中,电流传感器模块 INA226PRC 读取电流和电压值,并记录在内存中。 确定电流阈值,确定要测量的 IoT 设备在超过阈值后开始录制。
[mw_shl_code=arduino,true]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[indx] = amp; // 把电流值记录在存储器里。
        voltbuf[indx] = volt; // 把电压值记录在存储器里。
        if (++indx >= NSAMPLES) { //如果数据数超过样本数,则结束周期处理
            break;
        }
    }[/mw_shl_code]

测量周期和测量开始阈值允许使用 M5Stack LCD 和按钮进行选择。 记录的数据作为 csv 文件写入 SD 卡,并在 LCD 上显示。

[mw_shl_code=arduino,true]// 用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[NSAMPLES];
short voltbuf[NSAMPLES];

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[focused];
        }
        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[i * 10], 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[i * 10], 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[indx] = amp;  //
        voltbuf[indx] = 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[20];
    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()
{
}
[/mw_shl_code]

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-28 09:54 , Processed in 0.080278 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表