google-site-verification: google3bd66dd162ef54c7.html
FC2ブログ

Arduinoを使ったバッテリー放電器

コードレスフォンの子機の電池が空になりかけていることに気付きました。何しろ10年以上使っているので、電池がダメになってもおかしくはありません。電池はニッケル水素電池の単四を3個直列に束ねてコネクタが付いている物です。

さっさと新しい電池に交換すれば良いのですが、端子電圧を測定してみると 4.2Vくらいあります。この電圧を見る限り問題は無さそうな感じなのですが、コードレスフォンの子機の画面では電池の残量が最低表示(1/4表示)になっています。

なんだか釈然としないので、電池を完全放電させてリフレッシュしてみることにしました。どうせやるなら放電カーブを記録し、電池容量の測定もやってみることにしました。

そんなことで、Arduino を使って電池の放電器を作ってみました。

▼Arduino を使った電池の放電器
バッテリー放電器の全体
画面中央右下の緑色の物がニッケル水素電池(単四x3本)で、放電回路を経由して Arduino UNO に接続しています。

▼回路図
Arduinoを使ったバッテリー放電器
10Ω(5W) の負荷抵抗(RL) で電池を放電させるようになっていて、放電の ON/OFF はパワーFET (Q1) を D2 ピンから制御。電池電圧はアナログポート (A0) で測定。放電開始のタイミングをプログラムに知らせるためにスタートスイッチを用意、という回路になっています。なお、スタートスイッチはジャンパー線を手で持って GND(USBコネクタのシェルが便利) にちょんと触るだけなので、実際に部品としてのスイッチは使っていません。

▼実際の放電回路
放電器の回路
たったこれだけです。負荷には5Wのセメント抵抗を使っています。

▼プログラム
回路は簡単ですが、プログラムにはできるだけ機能を詰め込みました。
/* バッテリー放電器
FETをONにして放電させ、放電終止電圧以上の間バッテリーを放電させる
放電終止電圧は無負荷時電圧に対する比率で自動設定
2020.1.4 ラジオペンチ http://radiopench.blog96.fc2.com/ 
*/
#include <MsTimer2.h> // 時間はMsTimer2割り込みで制御

unsigned long t; // 経過時間(秒)
float loadR = 10.0; // 負荷抵抗
float Vcc = 5.00; // 基準電圧(CPUの電源電圧の実測値を設定)
float dR = 0.6; // 放電電圧終了電圧計算比率
long recInt = 10000UL; // 記録インターバル(ms単位の値で指定)
float battV; // 現在のバッテリー電圧
float openV; // バッテリーの解放電圧
float mAh = 0; // 積算電流
float mWh = 0; // 積算電力
float stopV; // 放電停止電圧
boolean runFlag = true;
volatile boolean tUp = false; // time Up フラグ

void setup() {
Serial.begin(115200);
pinMode(2, OUTPUT); // 放電制御 HIGH で放電
pinMode(3, INPUT_PULLUP); // 測定開始ボタン
pinMode(13, OUTPUT); // 放電中表示LED

// openV = analogRead(0) * Vcc / 1023.0; // 無負荷電圧測定(これは間違いで、)
openV = analogRead(0) * Vcc / 1024.0; // 無負荷電圧測定(正しくはこの式)
// 解説:analogRead関数の戻り値は Vref = 1024(0x400) としてマップされている。
//   しかし、1024は10ビットの範囲を超えているため、戻り値がこの値になることは無い。
//   戻り値が最大である 1023(0x3FF) に対応する電圧は、Vref - 1LSB = Vref * (1 - 1/1024)。

stopV = openV * dR; // 無負荷時電圧に対する比率で放電終止電圧を決定

Serial.println(); // 各種情報を出力
Serial.println(" ,BATTERY DISCHARGER");
Serial.print(" ,load R = "); Serial.print(loadR); Serial.print("ohm, Vcc(5V5) = ");
Serial.print(Vcc); Serial.print("V"); Serial.print(" ,openVoltage = "); Serial.print(openV);
Serial.print("V, stopVoltage = "); Serial.print(stopV); Serial.println("V");
Serial.println(" ,waiting start. (connect pin3 to GND for start)");

while (digitalRead(3) == HIGH) { // D3がLOWになるまで待つ
}
Serial.println(" ,Discharge start!"); // 測定開始メッセージ

Serial.println(" , sec., voltage, mAh, mWh");
digitalWrite(2, HIGH); // 負荷ON(放電開始)
digitalWrite(13, HIGH); // 放電中表示
delay(1000); // 初回測定まで一寸待つ
logOut(0); // 初回の測定結果を記録
MsTimer2::set(recInt, timer2IRQ); // 指定時間毎に割り込みをセット
MsTimer2::start(); // 割り込み開始
}
void loop() {
while (tUp == false) { // MsTimer2の割り込みが入りまで待つ
}
tUp = false;
t += (recInt / 1000); // 積算時刻を更新
logOut(1); // 測定結果をログ出力
}

void logOut(int mode) { // 測定結果をログ出力 (mode=0 は初回)
Serial.print(" , "); // タイムスタンプ文字列との境目を入れる
Serial.print(t); Serial.print(", "); // 経過時間(秒)表示

// battV = analogRead(0) * Vcc / 1023.0; // バッテリー電圧測定(これは間違いで、)
battV = analogRead(0) * Vcc / 1024.0; // バッテリー電圧測定(正しくはこっち)

Serial.print(battV, 3); // 電圧の値を出力

if (runFlag == true) { // 放電中で
if (mode == 1) { // 初回でなければ
mAh += (1000.0 * battV / loadR) * (recInt / (3600.0 * 1000.0)); // 積算電流計算
mWh += 1000 * (battV * battV / loadR) * recInt / (3600.0 * 1000.0); // 積算電力計算

Serial.print(", "); Serial.print(mAh); // 積算電流値を出力
Serial.print(", "); Serial.print(mWh); // 積算電力値を出力
} else { // 初回だけは積算値はゼロ
Serial.print(", 0.00, 0.00");
}
if (battV < stopV) { // 放電停止電圧以下になっていたら、
digitalWrite(2, LOW); // 放電停止して
digitalWrite(13, LOW); // 放電中表示OFF
runFlag = false; // 次回このルーチンを実行しないようにフラグを消す
Serial.print(" ,End discharge"); // 放電終了表示
}
}
Serial.println(); // ログの行末の改行
}

void timer2IRQ() { // MsTimer2の割り込み処理ハンドラ
tUp = true;
}

最初に電池の開放電圧 (openV) を測定し、その電圧に対する比率 (dR) で放電終了(停止)電圧 (stopV) を自動的に決定する仕掛けになっています。

スタートさせれば終了電圧になるまで放電させ、一定の時間間隔(現在は10秒)で測定結果をシリアルに流すようになっています。終了電圧になったら放電は止めますが、電圧の測定は続けるので、電圧が回復していく様子も記録に残ります。

電圧の基準には Arduino の電源電圧を使っています。標準値は5Vですが実際には使った電源やArduino のボードによって微妙違った値になっていることがあります。そこで、Vcc の変数に実際の値を手入力することで補正出来るようにしておきました。細かいことを気にしなければ、デフォルトの 5.00 のままで使っても大丈夫です。

あと、負荷抵抗や測定間隔などの測定条件も変数の値として定義しているので、カストマイズは簡単に出来ると思います。

このプログラムは、出力をパソコンなどに表示することを前提とした作りになっています。もちろん Arduino のシリアルモニタを使っても大丈夫です。

以下は TeraTerm で記録したデーターの例です。行の先頭には TeraTerm が挿入するタイムスタンプの文字列が入っていますが、この文字列とデーターとの境目の検出が簡単に出来るように、行頭にカンマを出力するようにプログラムしてあります。

▼出力例
[2020-01-02 14:43:55.565]  ,BATTERY DISCHARGER R = 10.00ohm, Vcc(5V5)= 4.91V
[2020-01-02 14:43:55.565] ,openVoltage = 4.22 ,stopVoltage = 2.53
[2020-01-02 14:43:55.565] ,waiting start (start when Pin3 connected to to GND)
[2020-01-02 14:44:05.612] ,Discharge start
[2020-01-02 14:44:05.612] , sec., voltage, mAh
[2020-01-02 14:44:06.610] , 0, 4.036, 0.00
[2020-01-02 14:44:16.610] , 10, 4.003, 1.11
[2020-01-02 14:44:26.609] , 20, 3.979, 2.22
[2020-01-02 14:44:36.609] , 30, 3.964, 3.32
[2020-01-02 14:44:46.640] , 40, 3.950, 4.42


[2020-01-02 16:53:45.036] , 7780, 2.520, 757.81 ,End discharge
[2020-01-02 16:53:55.035] , 7790, 3.019,
[2020-01-02 16:54:05.035] , 7800, 3.086,
7780秒の時点で放電停止となり、積算電流は751.81mAhとなっています。(この時点では積算電力(mWh)の値は出力していませんでした)

▼エクセルでグラフ化
エクセルで放電カーブを見る
綺麗な電池の放電カーブが取れました。

◆まとめ
ニッケル水素電池の放電器としては、簡単なアナログ回路だけで作った物を以前作りました。これはこれで便利なのですが、単セル電池用なので、今回のような組電池には使えません。そんなことで、作っておくと便利だと思います。使い終わったら、この記事を印刷したものと一緒にビニール袋にでも入れて保管しておけば、後で使う時にも便利かなと思います。

電池容量の値としては電流容量の値の mAh だけあれば大丈夫かも知れませんが、電力量である mWh の値も出力するようにしました。電池の内部抵抗が上がってくると端子電圧が下がるので、取り出せる電力量が少なくなります。この様子は電流容量の値にはあまり表れてこない場合もあるので、電力量も出力するようにしました。なお、この電力量は瞬時電力(10秒間隔)の積分で計算しているので、正確なエネルギー量になっているはずです。

この放電器を作るきっかけとなった、コードレスフォンの子機の電池ですが、結局はダイソーで単四のニッケル水素電池を買ってきて自作しました。そのあたりの話は別の記事で紹介したいと思います。

記事の中に書き漏らしたのですが、この回路で測定できる電池の電圧の上限は5Vまでです。(電源電圧が 3.3V の Arduino なら上限は3.3Vまで)になります。分圧回路を入れればもっと高い電圧まで使えるようになりますが、部品の数が増えて面倒なので今回はやってません。
関連記事

コメントの投稿

管理者にだけ表示を許可する

グラフ化出来て便利ですね

このような便利な装置をサクッと作ってしまうのは凄いです!
記録してグラフ化できるのが良いですね。
10Ωを可変すれば放電電流を好みに変更できますし。

私は ZB2L3 v2.0 を使っていますが、記録を外部に取り出せないので少し不満です。
少し改造すれば表示内容を出力できそうな気がするのですが技能が不足していて作れずです。

re:グラフ化出来て便利ですね

mytoshiさん、どうもです。

ZB2L3 v2.0 って知らなかったので調べてみました。安くて便利そうですね。資料見てたら欲しくなりました。

内部はPICのようなものを使っているのだと思います。積算電流の値はプログラムの変数として持っているのは間違いないですが、それを7セグの表示信号に変換して出力しているだけで、値として(シリアルなどで)外部に出すようにはなっていない気がします。いや、あるかもしれません、わかんないです。

1/1024で

本年もよろしくです。
で・・・
A/D値から電圧値を計算するところ、
「V = analogRead(0) * Vcc / 1023.0」で計算されていますが、
これ、まちがいです。

データシート、
http://akizukidenshi.com/download/ds/microchip/atmega328.pdf
この256ページ、24.7の式を見てもらえれば分かるように、
  ADC = VIN * 1024 / VREF
  VIN = ADC * VREF / 1024
で、1/1023ではなく1/1024が正しい値です。

10bitのフルスケール「0x3FF」が出てくるVINの電圧は
VREFより1/1024低い値(-1LSB)になります。
半値の0x200がVREF/2となることから、VREF/1023で
計算するとVREF=5Vだと2.5024Vとなってしまい、
VREF/2からちょいと誤差が生まれます。

1/1023 vs 1/1024

re:1/1024で

居酒屋ガレージ店主(JH3DBO)さんどうも、今年もよろしくです。

で、あああーー、たしかにおっしゃる通りで間違ってますね。

ATmegaのデーターシートには誤解が無いように説明まで入ってました。
0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB.
ADCの値が1023だったらアナログ値は Vrf - 1LSB なんですね。私、これがてっきりVref だと思ってました。つまり、値が1023だったら5V、というかVrefだと。

良く考えると、ADCの刻みが2のべき乗分の1になっていないとおかしいですよね。

昔は1024で計算していたのですが、どこかの時点で調べた結果、1023を使うようになったと記憶してます。

そんなことで、悪い作法を拡散させないように、記事のプログラムを修正しておきました。

openV = のところも

setup() 内の
  openV = analogRead(0) * Vcc / 1023.0;
の所もよろしくです。

re:openV = のところも

おっと、こっちも直さないといけませんでした。早速直しておきました。
お詫びのしるしに、なぜダメなのかも書いておきました。

Arduino のレファレンスに、0-5Vが0-1023 に相当する、と書いてあるのがこういう誤解が多い原因のような気がします。
カレンダー
05 | 2020/06 | 07
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 - - - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード