改造した秋月放電器とArduino+WSN256で作った放電特性計測装置がコレ…

ニッケル水素の電池放電特性計測

ニッケル水素の電池放電特性計測

単セルの電池の放電特性を計測するためWSN256のリファレンス電圧は2Vになるように調整し、電池ボックスを放電器とWSN256のアナログ0を放電器へ接続してます。

Arduinoをロガーにするためのスケッチ(プログラム)は以下の通り。

#include <Sleep.h>
#include <Wire.h>
#include <TwEEPROM.h>
#include <LiquidCrystal.h>        // LCDオブジェクト用のヘッダ・ファイル

#define LOG_STR_ADDR          0x500000  // device addr + memory addr
#define LOG_MEM_SIZE        0x2000    // 24LC64

#define LOG_ENT_SIZE          2
#define LOG_SIG_ADDR          (LOG_STR_ADDR+LOG_MEM_SIZE-4)
//#define LOG_READ_PEROPD       1000      // 1秒
#define LOG_READ_PEROPD       10000     // 10秒

// LCDが接続されているディジタル・ポートの論理番号の定義
#define LCD_RS 8
#define LCD_RW 2
#define LCD_E 3
#define LCD_D4 4
#define LCD_D5 5
#define LCD_D6 6
#define LCD_D7 7

// シフトレジスタ用ディジタル・ポートの論理番号の定義
#define SR_LAT 12
#define SR_DAT 6
#define SR_CLK 7

// ディジタル・ポート操作用マクロの定義
#define SR_DAT_1 digitalWrite(SR_DAT, HIGH)
#define SR_DAT_0 digitalWrite(SR_DAT, LOW)
#define SR_LAT_H digitalWrite(SR_LAT, HIGH)
#define SR_LAT_L digitalWrite(SR_LAT, LOW)
#define SR_CLK_H digitalWrite(SR_CLK, HIGH)
#define SR_CLK_L digitalWrite(SR_CLK, LOW)

// スイッチ関係のディジタル・ポート操作用マクロの定義
#define KEY_ENA digitalWrite(13, HIGH)
#define KEY_DIS digitalWrite(13, LOW)

// スイッチが接続されているディジタル・ポートの論理番号の定義
#define KEY_D0 4
#define KEY_D1 5
#define KEY_D2 6
#define KEY_D3 7

// LED用ディジタル・ポート操作用マクロの定義
#define LED1_ON  SRBuf|=(1<<0);SetSR(SRBuf)
#define LED1_OFF SRBuf&=~(1<<0);SetSR(SRBuf)
#define LED2_ON  SRBuf|=(1<<1);SetSR(SRBuf)
#define LED2_OFF SRBuf&=~(1<<1);SetSR(SRBuf)
#define LED3_ON  SRBuf|=(1<<2);SetSR(SRBuf)
#define LED3_OFF SRBuf&=~(1<<2);SetSR(SRBuf)
#define LED4_ON  SRBuf|=(1<<3);SetSR(SRBuf)
#define LED4_OFF SRBuf&=~(1<<3);SetSR(SRBuf)

// シフトレジスタの現在の出力値を保持する変数
unsigned char SRBuf = 0;

int analogpin = 0;

LiquidCrystal lcd(LCD_RS, LCD_RW, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

void setup()
{
    analogReference(EXTERNAL);

    pinMode(SR_LAT, OUTPUT);      // シフトレジスタ制御用ポートを出力に設定
    pinMode(SR_DAT, OUTPUT);      //
    pinMode(SR_CLK, OUTPUT);      //
    SR_LAT_L;                     // シフトレジスタのラッチ信号を"L"レベルに初期化
    SetSR(0x40);                  // シフトレジスタに1バイト出力

    Wire.begin();
    Serial.begin(9600);

    Logger_init();

    lcd.begin(16,2);
    lcd.setCursor(0, 0);
    lcd.print("Battery Logger");     // メッセージをLCDに表示
    lcd.setCursor(0, 1);
    lcd.print("Version 1.0");     // メッセージをLCDに表示
    delay(1000);
}

unsigned char BefKey = 0x00;     // 前回のスイッチ状態を保存する変数

void loop()
{
    unsigned char dat;

    static bool moni = false;
    static uint32_t last = 0;
    uint32_t now;

    dat = KeySense();           // スイッチ状態の読み出し(結果は下位4ビット)

    if (BefKey != dat)          // 状態変化のチェック
    {
        BefKey = dat;           // 変化があったとき、「前回値」を更新

        if (dat & (1<<0))       // SW1が押されているかチェック
        {
            LED1_OFF;
        }
        else
        {
            // push
            LED1_ON;
            moni = true;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Measuring on");
            Serial.println("Measuring on");
        }

        if (dat & (1<<1))       // SW2が押されているかチェック
        {
            LED2_OFF;
        }
        else
        {
            // push
            LED2_ON;
            moni = false;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Measuring off");
            Serial.println("Measuring off");
        }

        if (dat & (1<<2))       // SW3が押されているかチェック
        {
            LED3_OFF;
        }
        else
        {
            // push
            LED3_ON;
            moni = false;
            Logger_clear();
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Log Clear");
            Serial.println("Log Clear");
        }

        if (dat & (1<<3))       // SW4が押されているかチェック
        {
            LED4_OFF;
        }
        else
        {
            // push
            LED4_ON;
            moni = false;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Log Dump");
            Serial.println("Log Dump");
            Logger_printAll();
        }
    }

    Sleep.idle();

    now = millis();
    if ((now - last) > LOG_READ_PEROPD)
    {
        if (moni)
        {
            int summary = 0;
            for (int i=0;i<20;i++)
            {
                summary += analogRead(analogpin);;
                delay(2);
            }

            Logger_write(summary);
            print_volt(summary);
            Serial.println();
            lcd_volt(summary);
        }
        last = now;
    }
}

void print_volt(int summary)
{
    double t_value = (double) summary /20.0 /1024.0 * 2.0;
    Serial.print(t_value);
}

void lcd_volt(int summary)
{
    double t_value = (double) summary /20.0 /1024.0 * 2.0;

    lcd.setCursor(0, 1);
    lcd.println(t_value);
}

uint32_t Logger_wtptr;

void Logger_init(void)
{
    uint8_t sig[4];
    Serial.print("setup...");

    sig[0] = TwEEPROM.read(LOG_SIG_ADDR+0);
    sig[1] = TwEEPROM.read(LOG_SIG_ADDR+1);
    sig[2] = TwEEPROM.read(LOG_SIG_ADDR+2);
    sig[3] = TwEEPROM.read(LOG_SIG_ADDR+3);

    if (sig[0] == 'v' && sig[1] == 'o' && sig[2] == 'l' && sig[3] == 't' )
    {
        Logger_wtptr = LOG_STR_ADDR;
        for (uint32_t rdptr = LOG_STR_ADDR;rdptr<LOG_SIG_ADDR;rdptr+=LOG_ENT_SIZE)
        {
            int temp;
            temp  = TwEEPROM.read(rdptr+0) << 8;
            temp |= TwEEPROM.read(rdptr+1);
            if (temp == 0x7fff)
            {
                Logger_wtptr = rdptr;
                break;
            }
        }
    }
    else
    {
        Logger_clear();
    }

    Serial.println("done");
}

void Logger_write(int temp)
{
    uint32_t next = Logger_wtptr + LOG_ENT_SIZE;
    if (next >= LOG_SIG_ADDR)
    {
        next = LOG_STR_ADDR;
    }
    TwEEPROM.write(next+0, 0x7f);
    TwEEPROM.write(next+1, 0xff);
    TwEEPROM.write(Logger_wtptr+0, temp >> 8);
    TwEEPROM.write(Logger_wtptr+1, temp & 0xff);
    Logger_wtptr = next;
}

void Logger_printAll(void)
{
    Serial.println("print log begin");
    uint32_t rdptr = Logger_wtptr - LOG_ENT_SIZE;
    do
    {
        if (rdptr < LOG_STR_ADDR)
        {
            rdptr = LOG_SIG_ADDR - LOG_ENT_SIZE;
        }
        int volt;
        volt  = TwEEPROM.read(rdptr+0) << 8;
        volt |= TwEEPROM.read(rdptr+1);
        if (volt == 0x7fff)
            break;
        print_volt(volt);
        Serial.println();
        rdptr -= LOG_ENT_SIZE;
    }
    while (1);
    Serial.println("print log end");
}

void Logger_clear(void)
{
    Serial.print("clear eeprom...");
    TwEEPROM.write(LOG_SIG_ADDR+0,'v');
    TwEEPROM.write(LOG_SIG_ADDR+1,'o');
    TwEEPROM.write(LOG_SIG_ADDR+2,'l');
    TwEEPROM.write(LOG_SIG_ADDR+3,'t');
    TwEEPROM.write(LOG_STR_ADDR+0,0x7f); // end mark1
    TwEEPROM.write(LOG_STR_ADDR+1,0xff); // end mark2
    TwEEPROM.write(LOG_SIG_ADDR-2,0x7f); // end mark dammy1
    TwEEPROM.write(LOG_SIG_ADDR-1,0xff); // end mark dammy2
    Logger_wtptr = LOG_STR_ADDR;
    Serial.println("done");
}

void SetSR(unsigned char val)
{
    int i;
    SRBuf = val;
    for (i = 0; i < 8; i++)
    {
        if ((val & 0x80) != 0)
        {
            SR_DAT_1;
        }
        else
        {
            SR_DAT_0;
        }
        val <<= 1;
        SR_CLK_H;
        SR_CLK_L;
    }
    SR_LAT_H;
    SR_LAT_L;
}

unsigned char KeySense(void)
{
    unsigned char dat;

    // 共有信号を入力に設定
    pinMode(KEY_D0, INPUT);     //
    pinMode(KEY_D1, INPUT);     //
    pinMode(KEY_D2, INPUT);     //
    pinMode(KEY_D3, INPUT);     //

    KEY_ENA;                    // スイッチ・アクセス許可
    dat = PIND >> 4;            // スイッチ状態読出し
    KEY_DIS;                    // スイッチ・アクセス禁止

    // 共有信号を出力に設定
    pinMode(KEY_D0, OUTPUT);    //
    pinMode(KEY_D1, OUTPUT);    //
    pinMode(KEY_D2, OUTPUT);    //
    pinMode(KEY_D3, OUTPUT);    //
    return dat;
}

エレキジャクさんところのWSN256のサンプルプログラムと、「なんでも作っちゃう、かも。」で紹介されてた、EEPROMのライブラリィとソース参考に(というかまるまるコピー)して、スケッチをでっちあけました。みなさん有益な情報ありがとうございます。

LCDには、動作モードと電池の電圧を表示させ、4つのスイッチで動作を制御です。

  • 右から1番目の青色のスイッチは、ログ開始
  • 右なら2番目のオレンジ色のスイッチは、ログ停止
  • 左から2番目の黄色のスイッチは、EEPROMに記録済みのログのクリア
  • 左から1番目の赤色のスイッチは、EEPROMに記録済みのログをRS232Cにダンプ

この装置(笑)で計測です。
単4エネループが6本あります。中には4年落ちも混じってます。

エネループの放電特性

エネループの放電特性

特性の悪い4本が4年落ち。だが放電特性は揃ってます。単4ひとつで動作するmp3プレーヤーで使ってたので順調に劣化したのかも。残りの2本は2年落ちで携帯GPSで使ってた電池。放電特性にばらつきがあるみたい。にこいちで電池を使うと適正に充電できなくなる場合があるのでないかな? それで放電特性にばらつきが出るのかも…

放電時間から推定される放電容量は550mAhから600mAhというところ、放電電圧が低くともmp3プレーヤーや携帯GPSでは使えます。4年落ちでも使えるエネループは高性能なんだろうなぁ。