2023년 1월 29일 일요일

18650 배터리 내부저항을 ESP8266으로 측정하자

 1. 인간은 끊임없이 편의성을 추구하는 동물

 18650배터리의 내부저항을 수동으로 측정을 하였지만, 한두번 정도는 할 수 있어도 수십번 하려니 고충이었다. 무엇인가 편리함을 추구하기 위하여 다시금 머리를 굴렸다. 18650 배터리의 내부저항을 측정하기 위해서는 전압 측정이 필수이다. 이전에 ESP8266으로 20V 전압을 측정하는 방법에 대하여 글을 적었지만 이번에는 4.2V를 위하여 다시금 정리한다.

[ ESP8266으로 20V 저항을 측정하는 방법 ]


2. ESP8266 의  ADC (Analog to Digital Converter) Pin

 ESP8266에는 A0라는 단 한개의 ADC Pin이 존재한다. 

(두개 이상의 ADC Pin이 필요하다면 ESP32를 사용하면 된다. 일전에 내 블로그에 소개한 3채널 오실로스코프가 ESP32를 사용하였다) 

ESP8266의 A0 Pin은 최대 1V를 입력받도록 설계되어 있다. 물론 나는 ESP8266 그대로 사용하는 것이 아니라 Wemos D1 mini 의 제품으로 사용한다.  제조사의 홈페이지에는 아래와 같이 핀 설명을 하고 있다.



A0 Pin에 대해서는 max 3.3V라고 표시하고 있다. 어찌된 일이냐 하면 아래의 회도로들 보면 알 수 있다. (붉은색 테두리)




A0 핀으로 입력된 내용은 220KΩ와 100KΩ 두개의 저항으로 분압되어 ADC에 연결되는 형태이다.  3.3V가 A0에 입력되었다고 할떄 분압공식에 의거하여

ADC에 걸리는 전압 = 3.3V X ( 100 / (220 + 100) )
                          = 1.03125V

A0 Pin의 정밀도는 10bits 이며 전압을 인가하고 값을 읽으면 0V 일때 0, 분압이 없는 상태에서  1.0V 일때 1024의 값이 나온다고 할 수 있다. 다만 위의 분압 공식으로 유추해 보면 대략 3.25V부터 1024값이 나온다. 실제로 측정해보니 0V일때 20, 3.06V 부터 1024의 값이 나왔다.  이는 사용된 220K와 100K의 저항값의 오차에서 발생할 수도 있고, 측정핀에 존재하는 저항이 있을 수도 있다. 다양한 원인에 근거하지만 핵심은 3.3V 이전에 1024값이 나온다는 것이다. 3.06V는 내가 사용한 모듈에서만 그런 것이며, 제품별로 결과가 다를 수 있다. 같은 제품이더라도 모듈에 따라서 다를 수 있다.  핵심은 Wemos D1 mini의 A0 Pin으로 3V 이상 측정하려고 한다면 분압을 해야 한다는 것이다.


3. 18650 배터리 전압

 일전에 소개한 글에서 배터리의 내부저항을 측정하기 위해서는 전압 측정이 필요하다고 했다. 18650 배터리의 만충전압은 4.2V가 대부분 이지만 4.35V도 있다. 이에 따라 4.35V 입력시 A0 Pin에 들어가는 전압이 최대 3.25V가 되도록 분압해야 한다.

위와 같이 전압을 배분하면 배터리의 전압이 4.35V 일때 

4.35V X 220 / (100 + 220)  = 2.990V 의 전압이 A0에 입력된다.  사실 오른쪽 저항값이 전체 저항값의 약 75%만 차지하게 설계하면 문제없다. 보유한 저항이 있다는 가정하에 25K + 75K로 구성하면 된다는 이야기 이다. (물론 50K + 150K도 마찬가지 이다.) 사실 저항을 병렬, 직렬 해서 구성하면 저항값을 못 맞출 것도 없기는 하다. 보유한 저항에 맞춰서 구성하자.

일단 100K, 220K로 구성하는 경우

V = (A0에서 읽은 값) X  (100KΩ + 220KΩ) / 220KΩ   * 3.3V / 1024 
V = (A0에서 읽은 값) X  0.004692082111

만약 A0에서 읽은 값이 512 라고 하면 2.4024V라는 값이 수학적으로 계산이된다. 다만 저항의 정확도 및 배선 사이에서 생기는 저항을 고려한다면 한번쯤은 실측해서 보정을 해주는 것이 좋다. 

다만 아무리 계산을 열심히 해 놓아도 현실적으로 보유한 저항을 적당히 쓰기 위해서는 결국 100K 저항 3개, 43K 저항 1개를 사용하여 100K + 243K의 형태로 구성하였다.

[ 요렇게 준비했다 ]


위에서 적은 공식을 다시 적으면 

V = (A0에서 읽은 값) X  (100KΩ + 243KΩ) / 243KΩ   * 3.3V / 1024 
V = (A0에서 읽은 값) X  0.0045532988185318

납땜해서 실측한 값과 계산된 값을 비교해 보니 아래와 같았다. 

-

전압이 올라갈수록 실제 값과 차이가 커지는 것으로 보아 위의 공식에서 계산한 상수가 문제이다. 

엑셀의 힘을 이용하여 새로운 계산식을 도출했다.  추세선이라는 기능을 이용하면 편리하다. 아래의 그림에서 파란색 선은 실제 측정한값, 검정색은 추세선인데 거의 동일하게 위치해서 두개를 분리하여 보기란 힘들다. (즉 추세선이 잘 만들어 졌다.) 일반적으로 ADC 값은 전압에 따라서 선형 그래프를 보인다. 


이제 세로운 수식이 만들어 졌다.
V =  (A0에서 읽은 값) X  0.0052 - 0.0832
손으로 약간의 보정을 해서 아래의 수식으로 마무리했다.
V =  (A0에서 읽은 값) X  0.00522 - 0.0771
저항값으로 계산한 수식과 엑셀 추세선의 차이는 기울기가 좀 더 커졌고, Y 절편이 생겼다. 


4. 회로도 및 구현 내용

 무척 간단해서 약 20포인트 납땜이 필요한 회로이다. 




회로도는 이미 납땜한 후에 그렸다. 워낙 간단해서 회로도 그리기 전에 작업을 하였고 블로그에 기록하여 위하여 회로도를 그렸다. 

[ 나름 산뜻 전면부 ]

[ 얼기 설기 후면부 ]

[ ESP8266 아래에 저항 3개가 숨어 있다 ]


위의 사진에서 보이는 배터리 홀더는 지난번 블로그에 소개한 그것을 사용하였다.
또한 시멘트 저항이 4개 포함되어 있는 것을 알 수 있다. 이 회로는 향후 18650 배터리 방전 회로로 병행하여 이용할 계획이어서, 저항의 경우 8Ω 짜리 시멘트 저항을 4개 병렬 연결하였다.  4V의 전압을 인가하는 경우 시멘트 저항 1개당 0.5A가 걸리는 구조이다. 즉 최대 2A의 방전 능력을 가진다.  굳이 시멘트 저항을 여러개 병렬 연결하는 이유는, 단일 저항으로 2A의 전류를 흘려 보내면 생각보다 많이 뜨겁다. 4개로 분산해서 발열을 시키는 형태이다. 이정도로 연결해도 2A 전류가 흐를때 생각보다 뜨겁다. 

 회로에서 사용한 MOSFET은 55NF06인데, Data sheet 상에는 연속으로 50A가 가능하다고 기록되어 있지만 내가 알리에서 구매한 것들은 실제 그렇지는 않다. 방열판 없이는 개당 0.5A 정도가 적당하며 방열판이 있어야 개당 2A 정도가 가능하다. 그 이상의 전류를 흘려 보내면 손으로 잡을 수 없을 정도로 뜨거워진다.  작은 방열판이라도 각각 붙여줘야 하겠지만  MOSFET 가격이 방열판보다 싼 형태라 그냥 4개의 MOSFET을 병렬로 연결하였다. 
 회로도 및 구현한 사진에는 1개의 MOSFET만 보이지만 후에 3개를 추가하여 4개 병렬 연결하였다. 하나씩 추가 하면서 발열 상태를 체크하였는데 총 3개인 경우에도 발열이 부담되었고, 4개를 연결하니 안정적인 온도가 되었다. 

[ 4개 병렬 MOSFET ]

MOSFET을 똑바로 세워서 붙여 놓으니 살짝 열이 발생해서 위의 사진처럼 구부려서 부채 모양으로 전개했더니 열 발생이 거의 없었다. (처음부터 간격을 벌려서 납땜할껄 그랬다.)

"이렇게 계산한 내부 저항값은 참조용이며, 정확한 계산이라고 할 수 없다. "

5. 소스코드

 일단 시리얼 포트로 정보를 출력하였다. OLED를 붙여도 되겠지만(심지어 기판에는 OLED 올려 놓을 자리도 비워 놓았다. ) 일단은 시리얼 포트를 이용하기로 하였다.

소스코드 자체에 크게 어려운 부분이 없기 때문에 일부만 발췌하여 표기한다. (18650 배터리 방전 및 용량 체크 기능을 만들 예정이어서 그때 완성된 코드를 공개할 예정이다.)


void setup() {
  Serial.begin(115200);           // 시리얼 통신 속도 설정
  pinMode(MOSFET_PIN, OUTPUT);    // Mosfet control pin
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);    // Turn Off
  checkBattery();
}

float getVolt()
{
  float tmpValue = 0.0;

  // 측정 오차를 최대한 줄이기 위하여 CHK_CNT 만큼 측정해서 값을 나누어 사용한다.
  for (int i = 0; i < CHK_CNT; i++) {
    tmpValue += analogRead(A0);
    delay(5);
  }

  tmpValue /= CHK_CNT;
  g_fSensorValue = tmpValue;
  g_fVolt = tmpValue * 0.00522 - 0.0791;

  sprintf(g_strPrintBuf, "Volt = %.4f", g_fVolt);
  Serial.println(g_strPrintBuf);
  return g_fVolt;
}

void checkBattery()
{
  float onValue  = 0.0;   // 저항이 연결했을때 전압
  float offValue = 0.0;   // 저항이 연결되지 않았을때 전압
  float resistValue = 0.0;// 배터리 내부 저항
  float current = 0.0;    // 전류값
  digitalWrite(MOSFET_PIN, HIGH); // Turn On
  delay(500);
  onValue = getVolt();
  digitalWrite(MOSFET_PIN, LOW); // Turn off
  delay(1500);
  offValue = getVolt();
  current = offValue / 2.05;  // 전류값 측정
  // 1000은 밀리옴으로 변환하기 위한 값
  resistValue = (offValue - onValue) * 1000 / current;
  sprintf(g_strPrintBuf, "내부저항 = %.4f", resistValue);
  Serial.println(g_strPrintBuf);
}


소스코드의 핵심을 소개하자면 MOSFET을 켜고 전기를 흘려보낸 다음에 전압을 측정하고, 다시 MOSFET을 끄고 전압을 측정한다. 그뒤에 전압 차이를 전류로 나누면 내부 저항값이다. 
계산 과정이 궁금하다면 아래의 링크를 확인하자.


- 2023-02-06
소스코드를 일부 수정하였다. 
전류측정값을 고정으로 사용하였는데, 현재 전압에 의거하여 사용하도록 변경하였다.




댓글 없음:

댓글 쓰기

3단 6핀 스위치로 DC 모터의 회전 방향을 바꾸어 보자

1. 필요는 연구의 어머니 항상 느끼는 부분이다. 필요하지 않으면 연구하지 않으며, 필요하면 연구한다. DC 모터를 조건에 따라서 정방향 또는 역방향으로 회전시켜야 하는 필요가 생겼다. 처음에는 MCU 및 Relay Switch를 이용하는 방법을 생각...