Array 3710A and Rigol DP832 battery cycle testing in Python - Page 1
Forum section: electronics / testgear

author: sacherjj
author_rank: Frequent Contributor
timestamp: August 29, 2014, 10:01:47 pm
content: While looking at options for cycle testing Lithium batteries, I've not found exactly what I need. There are many battery cycling devices, but most are made to manually be run and start around $3k and go well past $10k.With some reading on here about various loads, I purchased a TekPower (rebranded Array) 3710A. I've been pretty happy with it, except for software. This week, I wrote a complete remote driver for the 3710A in Python. It supports all features published in the interface spec. I have to test the program set, start, and stop support against the actual load before I upload to Github.Since the loads each have a device number, I plan on using the same TTL serial connection for both loads I will be controlling (I just ordered a second 3710A). I assume I can just Y the USB to TTL serial adapter to connect to both. They both have 3.3V, GND, TX, RX on their DB9 connection. I worry about hooking both powers together. It seems like the safest setup would be connecting only GND, RX, TX. I'll look at that when my second 3710A arrives. The load really only responses to the 0x91 Read command. All others are assumed to work. It would be good if they would had built an acknowledgement protocol, but we have what we have.My charging plan is currently using my Rigol DP832. I will just set max current on max charge current and voltage at final charge voltage. Then I just have to cut off output when I reach my cutoff final current. This has works fine manually. I'm hoping to use the GPIO to toggle a 4PDT relay to swap batteries between the Loads and Power Supply.Our setup will be 4 batteries. Two will charge and rest after charge. Two will discharge and rest after discharge. Toggling the relay will swap which pair of batteries are in charge or discharge mode. We can cycle a cell a few times to get good capacity information, or run a cycle until failure program. Since we are using a non-standard chemistry, we want to do accelerated cycling life test.I'm going to be working on the DP832 interface in Python next week. I'm not sure if I'll be flushing out a complete support at first, but I hope to eventually get there. If I don't have any interface issues, we will be purchasing another DP832 to be used exclusively for this.I'll also be adding a PC interface to the on battery thermistors and possibly an accurate thermocouples tied to aluminum holder plates. We will eventually be running this 24/7, with the cells in a fire proof structure. The highest level software will be controlling the cycling and storing data in our database with each cycle.I'm hoping to gather a better understanding of how to tune our product, since we are shooting for a long term service life. This will allow me to vary time at constant voltage "trickle charge" and see how that affects cell life. My understanding is that too long at constant voltage at low currents just plates the electrodes, reducing capacity faster. Good experimental data is always better than stuff you read on the internet.I'd like feedback on this idea and also anyone that has either of the two pieces of equipment that is comfortable in Python and wants to use and help test my interfaces.

author: sacherjj
author_rank: Frequent Contributor
timestamp: August 30, 2014, 02:14:07 am
content: Posted the Array 3710 Python interface. I have not tested this current state against the actual device after adding the ArrayProgram support. Using my testing object, it looks to be working.https://github.com/sacherjj/array3710(Renamed repo)https://github.com/sacherjj/array_devicesLet me know if you have any questions. I'm going to work examples into my GUI control program, once I finish the DP832 interface.

author: nctnico
author_rank: Super Contributor
timestamp: August 30, 2014, 08:03:10 am
content: There is not much affordable out there for testing batteries if you don't want to pay through the nose. I recently built a large scale Lithium battery pack tester for a company which produces battery packs; it was cheaper to built something custom than to use 'standard' equipment.

author: saturation
author_rank: Super Contributor
timestamp: August 30, 2014, 12:34:48 pm
content: Nice. Looking forward to your work, I do a lot of battery testing, all types of chemistry, so this will help.

author: alex.forencich
author_rank: Frequent Contributor
timestamp: August 31, 2014, 05:48:48 pm
content: Python IVI should support the DP832. It may not be completely correct in terms of the protocol as I do not have a unit to test with personally, but it would not be very hard to hammer out any issues.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 02, 2014, 11:30:29 am
content: I've been testing the program support this morning and it works great. The one thing I'm surprised about is the time accuracy.I made a program that had 10 second long steps. I thought it would be pretty simple to have a 10 second delay in my Python testing script and see the changes fairly simultaneous. I'm seeing the load run through steps in just under 70% of the expected time. So a 10 second step is only just under 7 seconds. 30% error in timing is more than I expected. I'm not sure if this is a crystal change issue, or just a firmware mistake.I'll check timing on my second unit. It won't really bug me, because I don't plan to ever use program mode. It is surprising to see that much error in timing.

author: alex.forencich
author_rank: Frequent Contributor
timestamp: September 02, 2014, 05:22:45 pm
content: Wow, that is really far off. It definitely seems like using Python for the timing would be the best solution so you can precisely synchronize all of the instruments involved. Actually, you can do a bit better than a simple sleep call in python. I wrote a little logging script a while back that stores the current time at the start, then for each iteration it increments it by the time step (say, 10 seconds) and then sleeps for the difference between that and the current time. Seems to work well for minimizing long-term time errors, though it may have a little more jitter short-term.

author: nctnico
author_rank: Super Contributor
timestamp: September 02, 2014, 05:30:04 pm
content: I agree. For these purposes In usually use functions that query the system time to see if a certain period has elapsed than rely on sleep function. There is not much guarantee a sleep function actually sleeps for the specified period.

author: saturation
author_rank: Super Contributor
timestamp: September 02, 2014, 07:48:33 pm
content: I use the fastest reliable data capture speed, I think its 1 sec interval. Even if the actual data capture is off 100% of my setting, e.g. a 1 sec setting comes down to +/- 1 sec, all it does it plot data plot that I then use the curve to interpret the status. Its not good for capturing transients etc., but if needed I would have a scope also monitoring the power line.TekPowerup wrote custom code using Array units that performed well so there likely is a sweet spot for the most accurate data capture rate.http://www.techpowerup.com/articles/overclocking/psu/161Quote from: sacherjj on September 02, 2014, 11:30:29 am

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 12:16:21 am
content: I started fleshing out the DP832 interface today. I'm making it to the programming sheet, so it should be good for DP832(A) and DP831A.I received my second 3710A and tried just joining them with DB9 Y cable. I didn't leave it like that for long, because I still think linking the 3.3V lines isn't a great plan, if they start sourcing and sinking strangely, due to slight value differences.I can talk to both with the single link, but only reliably with the first one I start talking with. Some commands always work, but others don't. I didn't get a chance to put my scope on it, but the data looked good with using my Logic clone. I'll be walking back through my object to see if I have something that is class level across both instances, or something strange.I might need some delays in the system. Might just be trying to push the interface too hard. I'll look more into it tomorrow.

author: alex.forencich
author_rank: Frequent Contributor
timestamp: September 03, 2014, 12:41:53 am
content: Which interface are you using to control the power supply?

author: nctnico
author_rank: Super Contributor
timestamp: September 03, 2014, 01:03:45 am
content: When charging/discharging a battery a 10 second measurement interval is short enough. There should not be any transients unless the BMS is acting up.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 01:52:57 am
content: Quote from: alex.forencich on September 03, 2014, 12:41:53 amI'm starting with USB, because my 3710A control is USB. I'm building my python wrapper over pyvisa. Working fine right now, just need to flush out the objects and methods.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 11:23:48 am
content: My second TekPower 3710A is as close to the timing as the first as I can easily measure. I'm guessing that whatever loop they are using for 1 second delay in the firmware is 30% faster than they thought.If you write a program for the 3710A, take the time you want and divide by 0.7. That should get you close to reality.I did some quick testing with backfeed of 3.3V from the TTL Serial line. The back light and other electronics are not on the 3.3V bus, but the microcontroller and LCD display is 3.3V. I can "boot up" a second unit from the first, even when it is powered off. None of the load regulation works, because the higher voltage bus isn't powered.Pin out of cable is:DB91 - +3.3V2 - TX3 - RX5 - GND

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 01:39:10 pm
content: Custom splitter built and it works great. I have a primary connection and a secondary connection. Primary load MUST be on to power the Load side opto-isolated electronics.DB9 from 3312 TTL-to-USB Adaptor1 - +3.3V2 - TX3 - RX5 - GNDDB9 from Primary Load1 - +3.3V2 - TX3 - RX5 - GNDDB9 for 1-N Secondary Loads1 - NC2 - TX3 - RX5 - GNDThe connection limit would be determined by line noise, I would think. You should be able to hook up as many as you want, as long as you are comfortable with the maximum query speed allowed by time sharing the serial data.I setup a loop to shove current change commands and request an update, with no delay. I'm updating current on load0, then load1, then update load0, then update load1. This works as fast as I can send it at 9600 baud.I have not been able to get 100% reliable communication above 9600 baud. At 19200, I get 1 failure in 20 at best. At times, once a failure occurs with no pause, it is locked into 0 byte return mode. It feels like the micro-controller can't quite handle the data speed without some breaks.At 38400 baud, I get up to 70% failures at times. I tried adding 50-100 ms delay between each command, but this only marginally increases 19200 reliability, at a decrease of throughput compared to 9600. So I'll only be running my loads at 9600 baud.Due to the address being in each command, all loads on the bus must parse each packet. Once a load is jammed up during continuous packet storm, it remains jammed up. The application software will have some pauses, because it will be single threaded and talk to power supply and other loads. However, if you are interfacing only to the load in a high polling scenario, it is advisable to put frequent wait states to help recover from any failure due to loading.

author: alex.forencich
author_rank: Frequent Contributor
timestamp: September 03, 2014, 04:11:19 pm
content: Stick a scope in there and measure actual baud rate. It is possible that the timing issue affects the serial interface as well.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 05:43:03 pm
content: I flushed out some examples over lunch and cleaned up the naming for the Array interface. I think this is going to stay for a while like this. Currently only Python 2.7 compatible.I renamed the repository to make the namespaces more logical for import.https://github.com/sacherjj/array_devices

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 03, 2014, 10:27:22 pm
content: Quote from: alex.forencich on September 03, 2014, 04:11:19 pmI didn't have time to hook up the scope. But I wrote a couple line program to try talking with it at different baud rates, while I did other things. (Brute force takes no time from me.)Found something I didn't expect. This is a list of baud rates on the PC that created error free communication with the load at each load baud rate. I walked from baud_rate/3 (rounded to nearest 100) to baud_rate*3 by 100. 20 attempts are made at each baud if the initial connection actually works, error counts are out of 20.4800(L1): 4700-5200 (All 0 errors)4800(L2): 4700-5200 (All 0 errors)9600(L1): 4000-4600, 5300-6400, 7600-12400, 15100-17400 (All 0 errors)9600(L2): 4000-4600, 5300-6400, 7600-12400, 15100-17400 (All 0 errors)19200(L1): 17500-24000 (~30% with 1 error, 1 with 2 errors)19200(L2): 17500-24000 (~20% with 1 error, 5% with 2 errors)38400(L1): 35900-44200 (Errors of 1-4 throughout range, pretty evenly spread, 20% with 0 errors)38400(L2): 35900-44200 (same distribution of errors as L1 at this baud rate)This proved to me that there is no baud that is 100% reliable over 9600. But I'm not sure how in the world the various baud ranges actually communicate with the load when the load is at 9600 baud. I'm going to have to scope the exchange in the 4k and 16k ranges, I don't understand how this works.The other thing I found interesting is that they are very reproducible between loads. That isn't surprising, because it is most likely controlled by the accuracy of the crystal.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 05, 2014, 05:00:38 pm
content: I looked around for existing DP832 interfaces in Python. I found Python IVI which is more complex than I would design, but existing. The DP832 interface was broken, but I fixed it yesterday and those changes were merged with the main project. It doesn't have full support to all features yet, but has the primary methods needed. I'll flush out more support as I need it.https://github.com/python-ivi/python-iviThis will allow people to use other supplies with the programs I'll write, but just making an ivi driver for it. One thing that isn't currently supported in ivi is electronic loads. There are plans to support them, but it means the work for my 3710 interface isn't wasted.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 06, 2014, 12:01:04 am
content: Quote from: sacherjj on September 03, 2014, 05:43:03 pmI committed updates with Python 3 compatibility. This interface should now work for both Python 2.7 and 3+.

author: sacherjj
author_rank: Frequent Contributor
timestamp: September 10, 2014, 12:58:13 am
content: I'm working on the library to do battery cycling. I've structured it so you can supply charger and discharger objects. This should allow charging by any computer controlled power supply and discharging by electronic loads, or just power resistor and DMM. I also have a switching object. This will handle toggling any additional hardware between charge and discharge. In the case of resistor and DMM, it would involve controlling a relay to disconnect the battery at discharge.I will allow the controller to run with an out of phase secondary controller. I will have two batteries connected to a 4PDT relay that hooks one up to the power supply as the other is to the load, and vise versa. This allows me to optimize equipment while completing cycles. In my setup, charge time is faster than discharge time, so the battery will have a rest state prior to charge and after charge.I have added array_devices to the Python Package Index. To install the interface to Array 3710A, with pip installed for Python, run:Code:pip install array_devicesThen you can follow the examples on the github I posted earlier.