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

vany5921 发表于 2020-4-3 11:04

【搬运】使用 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]
查看完整版本: 【搬运】使用 M5Stack 测量 IoT 设备的电流消耗