An device-side implementation of the new modes from the Quick Charge 3.0 protocol to provide voltages from 3.6-20V in 0.2V increments
To make the experience fit your profile, pick a username and tell us what interests you.
There's really nothing in this post that you'll need in order to replicate the QC3.0 hack, it's mainly just describing some of the silly problems I ran into.
A couple of days later, my new power bank arrived! I plugged it in to ensure it was ready to go before I began experimenting.
Using C to program a microcontroller permanently is great, but for quick development and testing, I greatly prefer something like Python. It's great to be able to use the command line to try out an idea in one or two lines as opposed to write, compile, reflash... So I chose to use an ESP8266 module with MicroPython for my experiment. I got it on eBay for around $5. I have a few of this particular dev board and I think they're great. The pins are broken out to standard 0.1" headers, indicator LED for each GPIO, voltage regulator onboard, a jumper block for setting the flash into programming mode. One little improvement I make is to remove the little battery box and replace it with a standard USB connector; just plug it into any USB socket and your board is powered up.
First, I did a quick and dirty re-implementation Hugatry's QC2.0 code in Python. Hit my first major problem: it didn't work. All I could get out of the power bank was the normal 5V. Was the problem with my code? My circuit? The power bank itself? Time to troubleshoot. I stepped through Hugatry's QC2.0 video and decided to validate my circuit by disconnecting the ESP8266 and doing the handshake by hand with jumper wires. Nope, didn't work. His document said "Device should allow the voltage on D- -line to drop below 0.325V, for example by keeping D- disconnected." When building my circuit, I had interpreted that as "D- needs to go low" with the "keeping D- disconnected" as just a suggestion. In my implementation, I was tying D- to ground. Turns out, that doesn't work -- it must be left floating. After correcting that, the voltmeter showed 12V now. The circuit and power bank worked.
But it still didn't work when hooked up to the microcontroller. There must be a bug in my code. I pored over the code, tweaked some things, still no success. I hooked the voltmeter into the circuit to observe it in action. Bmm, that's weird: when I'm trying to set the D- pin to high impedence ("disconnected"), I'm actually getting a residual voltage of 0.1V. That's when I realized: those nice LEDs on my dev board hooked up to each GPIO, they were also providing another path for current from my GPIO pin. I desoldered the current-limiting resistors from the two GPIOs I'm using for control of D- and... it works! I've replicated the existing QC2.0 hack, finally, with a bit extra logic to allow for a new state that Hugatry was not using (3.3V on D-) that was required for the two new modes.
Now to move on to the new features contained in QC3.0. At this point, I wasn't really hopeful that it would work. The CHY103 datasheet says "At this point the Quick Charge 2.0 handshake followed by the Quick Charge 3.0 handshake can take place", and I haven't found any documentation of this new handshake for QC3.0. But what the heck, I'll try the new entries in the state table for the two new modes anyway, with only the QC2.0 handshake. The 20V mode: failure. Continuous mode: no obvious signs of success or failure. Try out an "increment" pulse on D+, with my wild guess of 200us pulse width. And, shockingly, it WORKED! I can't guarantee that this works for all QC3.0 power sources, but it worked on this one.
I incremented and decremented until the voltage stopped changing. My particular device accepted decrement pulses until it should be (nominally) 3.6V, though the voltmeter continued to display 4.27V. I suspect that's the actual output voltage of the LiPo cells inside the device,...Read more »
While I waited for it to arrive, I tried to hunt down the technical details about how the QC3.0 protocol worked. Hugatry's blog was a great starting point for QC2.0, but I needed to learn how to set the other voltages as well. Given his success in mining the protocol info from datasheets, I went looking for the same. I found the datasheets for NCP4271-D and CHY103, which gave me the additional info that I needed.
The NCP4371-D datasheet shows the appropriate states to put onto D+ and D- to enable both the new discrete 20V mode, and "Continuous Mode" where the voltage is adjustable in 0.2V increments. These appeared to be relatively straightforward -- just previously unused voltage states for the same table that Hugatry used for QC2.0. While in Continuous Mode, the voltage is adjusted up or down in 0.2V increments by briefly pulsing the D+ line high (to increment up) or pulsing the D- line low (to increment down). The pulse times are given as T_ACTIVE, T_V_CONT_CHANGE_SINGLE, and so on, but the actual values are not given anywhere. The CHY103 datasheet mentions a value for "Continuous Mode Glitch Filter Time" of between 100 and 200 us, so that's what I decided to try.
Here's the table of states for Quick Charge 3.0 from the NCP4371 datasheet, page 9:
|D+ Line||D- Line||Output Voltage|
|0.6 V||0.6 V||12 V|
|3.3 V||0.6 V||9 V|
|0.6 V||3.3 V||Continuous Mode|
|3.3 V||3.3 V||20 V|
|0.6 V||GND||5 V|
As you can see for Continuous Mode, D+ is held at a low voltage and D- is held at a high voltage. Continuous Mode is the state wherein we can adjust the voltage in 0.2V increments. Again per the NCP4371 datasheet, page 9, The method to do this is to briefly pulse D+ high to increment, or to pulse D- low to decrement.
After recently reading about a hack to get a Quick Charge 2.0 (QC2.0) USB device to output 9V and 12V instead of the typical USB voltage of 5V, I read up more on the standard and (along with several others) noticed that Quick Charge 3.0 (QC3.0) offered support for many more voltages.
QC3.0 will supposedly supply any voltage asked for between 3.6V and 20.0V in 0.2V increments. I have an old boom box radio that takes 10x D Cell batteries, which are now quite expensive (and also heavy!), so I thought QC3.0 would let me easily get the equivalent 15V required to be a drop-in replacement. I ordered a QC3.0 power bank on Amazon to give it a shot.
Get the MicroPython firmware running on your ESP8266. Here are some instructions if you've never done that before. You will need a module with at least 1 MB of flash and 3 free GPIOs. Your old ESP-01 probably isn't going to cut it, sorry.
Download the Python code from my GitHub: dracode
Once you've got the firmware running, upload the Python code. I use webrepl_cli.py to upload my files.
I used a standard USB-A connector that would plug into the QC3.0 port on my power bank. I just cut an old cord and re-used it. The black wire is ground, red is positive, white is the "D-" line and green is the "D+" line.
I used 1kΩ and 4.7kΩ resistors to create voltage dividers that I could control with a microcontroller. With a voltage of 3.3V on top of the bridge, that would give me right around 0.6V in the middle. According to the data sheet I used, the signals levels we need are: 3.3V, 0.6V, 0V, or disconnected.
I used a 3.3V microcontroller in the project. It's VERY IMPORTANT that you do NOT hook the microcontroller up to the positive wire coming out of your QC3.0 USB connector! We're about to ask the power bank to provide up to 20V on that line, which will surely fry your microcontroller. I used a separate 3.3V power source in this proof of concept project, but in real use you would probably want to use a 3.3V voltage regulator to safely get power from the variable-voltage QC3.0 source.
Plug in your QC3.0 power bank/charger/whatever and turn it on (if it has an on button).
Run the example.py script you downloaded from GitHub.
Become a member to follow this project and never miss any updates