Remanufacturing a 12V power tool battery

Cordless power tools often die not because they’re worn out but because the battery fails, or only holds a charge for a short time. Every tool’s battery pack seems to be different, either from the sheer cussedness of consumerism making you buy a new tool, or because nobody has standardised battery packs, You can fight back, because though the battery packs are all sorts of sizes, the parts inside are usually drawn from standard sized parts, they’re merely packed in different ways.

I had a cordless strimmer, once advertised years ago in the Guardian, from a no-name supplier with two battery packs. Both of which are now duff – a strimmer is a fairly hard life for the battery packs with about 50-100W. Here’s how I remanufactured the battery packs. These are NiCd battery packs – the key issue to to replace like with like. You can replace NiCd with NiMh but don’t cross any other chemistries.

A possible source of raw materials
A possible source of raw materials

Continue reading “Remanufacturing a 12V power tool battery”

how to measure AA battery capacity

Poundland in the UK sell batteries branded by Kodak, but the cheapest ones are zinc chloride. I thought ZnCl has gone out of use decades ago, but it is surprising how often people are penny-wise and pound foolish. Particularly in pound shops 😉

Cheap Poundland batteries are in fact twice as dear to use!
Cheap Poundland batteries are in fact twice as dear to use…

So I am looking to make something to track the discharge of a battery. This is also useful to qualify NiMH and NiCd batteries – these fade with time and it is good to know what sort of capacity is left. the expensive ones!
…as the expensive ones!

Every minute it reports the voltage and current from the batteries running through a 2.5V torch bulb, the third bulb is maintained at 2.5V to provide a reference. It transmits the signal using radio to a datalogger. I got a camera to take a picture every 15 minutes, as a video the results are reasonably clear. A picture tells a thousand words – the Zinc Chloride batteries are crap

The left-hand bulb is powered by the ‘cheap’ battery that Poundland sell for 9p, the middle is powered by the ‘dearer’ alkalines they sell at about 17p.

Bulb measurement schematic
Bulb measurement schematic

I have a Ciseco OpenKontrol Gateway as a datalogger. It stores data to a SD card, but there is no law saying you couldn’t send it to some kind of IoT host like Xively. So all I need is the interface to the batteries to track the voltage and current; I used a 2.5V torch bulb for the load because it does not seem unreasonable to me to expect batteries to be used in a torch, I use two batteries in series, which seems to be the most common configuration in a torch. On the way to ground I use a 1 ohm resistor so I can measure between ground and the top of the bulb the battery voltage and between the resistor and ground the voltage corresponding to the current. Using 1 ohm makes the calculation easy, and also drops about 0.3V so the bulb is run at 2.7V at the start.

Now if you are looking to do this in a big way then these guys at Battery Showdown are doing this is a more rigorous way but their hardware is dearer. I’m also not absolutely sure that a constant current discharge is typical of many loads. something like a digital camera with a switch mode PSU actually increases its peak load with decreasing battery voltage because it is more a constant power device and needs to draw more current at a lower voltage to run the focusing bits. Something with a linear voltage regulator is a constant current to the dropout voltage of the regulator, but you don’t often see linear regulators in battery-powered kit.

The bulb isn’t ideal either, with a varying resistance with temperature, so I’d probably favour using resistive loads, and getting four/five battery channels instead of two. But the visuals were great for the video.

If you have a PC that can take RS232 then simply wire the output of the PIC to the PC serial port via a Max232 chip or single transistor inverter if you don’t need the radio connection. Since I have the datalogger logging other sensors it seems easy enough to use it as is.

I used a PIC16F870 that has five A/D channels – this gives me two battery channels. Each battery channel has one A/D port connected to A in the diagram, another one to B and 0V is connected to C. The voltage at B is the same as the current since R is 1Ω and the voltage at A is the battery voltage itself. The 10-bit A/D converter goes up to 1024, by setting the reference voltage to be the PIC power supply at the high end and PIC ground at the low end I made the maths easier by setting the PIC supply voltage to 5.12 V (this is within the PIC operational voltage spec of 4 to 5.5V) in which case a straight divide by two gives V in * 100, and matches the requirements acceptably.

Doing this for the Poundland batteries shows the difference between the alkaline and the ZnCl batteries

alkalines give about three times the runtime than ZnCl
alkalines give about three times the runtime than ZnCl

If we take the service life as down to 2V from 3V initially, then you get 1.7 hours from the cheap ones and 5.8 hours from the alkalines.Alkaline cost/hr is 17*2/5.8=5.9p/hr and the cheap batteries cost 9*2/1.7=10.6p/hr.

As a result you pay about twice as much to run something using the cheap Poundland batteries, and you get to replace the batteries three times as often. Saving money therefore costs you a fortune in aggravation and generates more waste. You also have this sort of problem if you don’t pay attention and leave the used batteries in too long.

cheap batteries consume the zinc outer casing, leading to this
cheap batteries consume the zinc outer casing, leading to this

The device offers an insight into the capacity of rechargeable batteries which is the more useful feature of it.

a very old pair of 500mAh nicads and a pair of Uniross 2300mAh NiMh
a very old pair of 500mAh nicads and a pair of Uniross 2300mAh NiMh

I am not really sure I understand the pathology behind the second wind the NiMH cells seem to have. I’d discard it as a fault in reading except that – the current shows the bulb did not blow or become loose, and the A/D and voltage reference is multiplexed, so the error should have shown elsewhere.  will repeat this and see if it happens again – probably not as this battery has been totally flattened so it is not the same battery.

For rechargeables I will modify this rig to take four individual cells and discharge through four resistors. That way I don’t back-charge any weak cells, and I don’t need to measure the current as the terminal voltage and Ohms Law will tell me the current. If I run 2.7Ω I will run about 440mA.



PIC battery tester schematic

JAL code

The JAL code for the PIC is 1311_datalogger_v06.jal

-- JAL 2.4i
-- derived from univ-sensor_v06
-- Datalogger
-- adjust power supply to 5.12V using the LM317 regulator.
-- internal Vref = VCC = 5.12V
-- Can just divide ADC by 2 and shift dec point by two places
-- v06 12 Nov 2013 RM
-- CC BY-SA 3.0
-- serial port designed for use with a Ciseco XBBO board and radio ( Google it)
-- can also use w/o radio via a Max232 or a FTDI adapter instead
-- radio is powered down while ADC measurement made to avoid loading regulator voltage

include 16f870
pragma target clock       4_000_000
pragma target osc    HS   ; use a 4MHz crystal or resonator
pragma target WDT    ENABLED
pragma target PWRTE  ENABLED
pragma target BROWNOUT    ENABLED
pragma target LVP    DISABLED
pragma target CP    DISABLED
pragma target CPD    DISABLED
-- HS, -BOD, ,-LVP, -WDT, -CP  = 0x3F22
;pragma target fuses       0x2007

include pic_general
include print
include format

-- set all interrupt sources disabled

-- ADC includes delays so don't include this myself

-- V2 delay's
-- include delay_any_mc

-- more delay functions
-- include extradelay

-- the analogue pins
pin_a0_direction = input
pin_a1_direction = input
pin_a2_direction = input
pin_a3_direction = input
-- there is no a4 analogue channel
pin_a5_direction = input

pin_c7_direction = input
pin_c5_direction = output
pin_c6_direction = output

pin_A4_direction = output          ; this is open drain so 3V3 pullup comes from XRF
alias xrf_sleep is pin_A4          ; pull this low to start the XRF, let go and float up to enter sleep
xrf_sleep=low                      ; must enable with ATSM2

-- now configure ADC  (this is a 10-bit ADC)

const bit ADC_HIGH_RESOLUTION = true
const byte ADC_NCHANNEL = 1
include adc

-- ok, now setup serial, we'll use this
-- to get ADC measures

-- all this is because the file is not right for serial hardware  8/5/13
-- end of
-- all this is because the file is not right for serial hardware


OPTION_REG = 0b00001111 ; PS asllocated to WDT and slowest rate selected (20ms*128)
PEIE = high
TMR1IE = high
T1CON=0b00110001        ; set timer 1 running internal osc 8xprescaled
CCP1CON=0b00001011      ; reset tmr1 on match, kicks off an A/D conversion
                        ; these next values seem to call a period of 10s from previous project
CCPR1H=244              ; decimal
CCPR1L=36               ; decimal
-- so count is 244*256+36 = 62500 At 4MHz each tick is 1uS, prescsaled by 8 to 8uS ticks.
const byte cyclesmax=60*2   -- how many times to count 0.5s ticks

var word mincounter = 0
;var byte lastTMR0=0
;TMR0=0   -- clear at start

-- ok, now setup serial;
const serial_hw_baudrate = 9600
const bit usart_hw_serial = TRUE
const bit lowspeed=true
include print
include serial_hardware

const byte booted[] = "aVVSTARTED--"
const byte prefix[] = "aVV"
const byte line1[] = "+++"
const byte line2[] = "ATSM2r"
const byte line3[] = "ATACr"
const byte line4[] = "ATDNr"
const byte line5[] = "ATID0AFFr"
const byte vpfx[]= "VLT"

procedure WaitOnOk() IS
-- state machine - aim is to hang and
-- if this doesn't get to 2 the WDT will start things over after 2 sec
-- if it gets to 2 it clrwdt anbd returns

var byte state=0
asm clrwdt
while state !=2 loop
if serial_hw_data_available then
   if state==0 & serial_hw_data == "O" then
      state=1                        ; this is the only way to get to state 1
   end if
   if (state == 1) & (serial_hw_data == "K") then
      state=0                        ; because O was followed by something that wasn't K
   end if
end if
asm clrwdt
end loop

end procedure

procedure justify(word IN myval)  IS
-- value is going to be 0..1024 (in reality 0..512)
-- hundreds are integers, ie 2 d.p.
-- assumes radio is up and running
  format_word_dec(serial_hw_data,myval,3,2)  -- 0   .23
end procedure

function get_volts() return bit  is
  var word reading1
  var word reading2
  var word reading3
  var word reading4
  var word reading5
  asm clrwdt
  -- get ADC result, high resolution
  reading1 = adc_read_high_res(0)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading2 = adc_read_high_res(1)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading3 = adc_read_high_res(2)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading4 = adc_read_high_res(3)/2
  asm clrwdt
  -- get ADC result, high resolution
  reading5 = adc_read_high_res(4)/2
  asm clrwdt
  -- send it back through serial
  xrf_sleep=low                                    -- start the radio a little before the data will be sent.
                                                   -- may want to change the LLAP prefix and comma del if not using radio
  asm clrwdt
  asm clrwdt
  delay_100ms(5)                                   -- no need for this dela if going straight out on serial
  asm clrwdt
  delay_100ms(5)                                  -- allows the LLAP data to be received properly
  asm clrwdt
  asm clrwdt
  asm clrwdt
  asm clrwdt
  asm clrwdt
  delay_100ms(2)                                         -- let the signal be transmitted and buffer empty
  xrf_sleep=high                                   -- stop the radio ftre the original data sent
  return true

end function


asm clrwdt
asm sleep                           ; use the WDT to delay about 2s to
                                    ; boot the sensor  and radio
asm clrwdt

-- now initialise the XRF to the system network ID and enable sleep mode
-- clear out any pre-existing RX buffer
var byte rxd
while serial_hw_data_available loop -- check if data is ready for us
  rxd = serial_hw_data          -- get the data
end loop
rxd=" "

if true then                        ; loop is simply to enable/disable
                                    ; if you have no radio then use if false then
asm clrwdt                          ; this setup routine for testing
                                    ; setup the radio using AT commands
print_string(serial_hw_data, line1) ; send +++
print_string(serial_hw_data, line5) ; change network with ATID0AFF
print_string(serial_hw_data, line2) ; enable sleep mode with ATSM2
print_string(serial_hw_data, line3) ; and use it - send commit
print_string(serial_hw_data, line4) ; ATDN we are done configuring
xrf_sleep=high                                    -- power down the radio
end if

forever loop

asm clrwdt
if CCP1IF then
end if

if mincounter >= cyclesmax-1 then
   var bit b = get_volts()
end if

end loop