Simple 6X USB Charger with Current Monitor

This is a simple 6 port USB device charger with a individual current monitor on each port. The charging current is indicated using RGB LEDs. Blue means slow charge (under 250mA), green means 250mA to 750mA, red means over 750mA, and purple means over 1500mA (for tablets). This circuit involves an ATmega328P (if you do hobby electronics, I bet you have plenty spares of these), INA169 (check out this breakout board), and a OKR-T10-W12.

While this project is not as impressive as my other projects in terms of difficulty, I soldered and programmed this entire project in just one day with mostly spare parts and no pre-planning. I hope to inspire you to solve everyday problems by DIY instead of buying stuff!

The problem I faced was that I had too many devices to recharge at once, not having enough chargers and not having enough AC jacks. Also my new Sony wireless headset was being picky about both the cable I use and the charger I use. I decided to troubleshoot this problem by building this tool.

Construction is mainly done on perfboard, using through hole components mostly (where I must or where I need mechanical strength), mixed with surface mounted components whenever possible (0603, 0805, and SOT-23-5). Wiring is done using 30 gauge Kynar coated wire, with thicker wiring where high current is needed. A decently capable wall-wart is needed, anywhere between 4.5V to 12V is acceptable, and it must be able to supply enough current for all the devices to be charged. A DC/DC converter is used to increase efficiency, so a 12V wall-wart supplying 4A can actually charge about 8A total. A piece of acrylic plastic on the bottom prevents the wiring from being damaged when the circuit is sitting on a desk or being handled. The D+ and D- signals have the appropriate resistors to enable high charging rates on Apple devices (which is also compatible with Sony, Samsung, and other brands).

Extra features I did not want to include: reverse polarity protection, input fuse, individual over-current cutoff, LED dimming, etc. I know some people might suggest these to me, I am aware of these possible features but I didn’t want to include them due to cost and time and convenience.

Here is the schematic and firmware C code

#include <avr/io.h>

#define THRESHOLD_HYSTERISIS 1
#define THRESHOLD_MIN 1
#define THRESHOLD_25MA 2
#define THRESHOLD_50MA 4
#define THRESHOLD_250MA 19
#define THRESHOLD_500MA 39
#define THRESHOLD_750MA 58
#define THRESHOLD_1500MA 116

#define LED_COMMON_PORTx PORTB
#define LED_COMMON_DDRx DDRB
#define LED_COLOUR_PORTx PORTD
#define LED_COLOUR_DDRx DDRD
#define LED_RED_BIT 5
#define LED_GREEN_BIT 6
#define LED_BLUE_BIT 7
#define LED_ALL_BITS (_BV(LED_RED_BIT) | _BV(LED_GREEN_BIT) | _BV(LED_BLUE_BIT))

uint8_t last_state[6] = { 0, 0, 0, 0, 0, 0, };

void adc_start(uint8_t chan)
{
	ADMUX = _BV(REFS0) | _BV(ADLAR) | (chan & 0x07); // setup reference, result alignment, and select channel
	ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADPS2); // enable and start ADC conversion with appropriate clock prescaler
}

uint8_t adc_read()
{
	loop_until_bit_is_set(ADCSRA, ADIF); // wait for conversion to finish
	ADCSRA |= _BV(ADIF); // reset the flag
	return ADCH; // read back (already aligned 8 bits)
}

void setup()
{
	// setup the pin directions
	PORTC = 0;
	DDRC = 0;
	LED_COMMON_PORTx = 0;
	LED_COMMON_DDRx |= 0x3F;
	LED_COLOUR_DDRx |= LED_ALL_BITS;
	adc_start(0); // start the first read
}

void loop()
{
	uint8_t i;
	for (i = 0; i < 6; i++)
	{
		uint8_t a = adc_read(i);
		adc_start((i + 1) % 6); // start the next read (this technique gives good timing because we wait less, timing means even LED brightness)

		uint8_t old_state = last_state[i];
		uint8_t new_state = 0;
		
		// apply hysterisis logic to combat noise (not very effective in real life but I tried)
		// TODO, add LPF filter or lower sample rate
		if (a == 0) {
			new_state = 0;
		}
		else if ((old_state == 4 && a > (THRESHOLD_1500MA - THRESHOLD_HYSTERISIS)) || (old_state == 3 && a > (THRESHOLD_1500MA + THRESHOLD_HYSTERISIS)) || (old_state < 3 && a > (THRESHOLD_1500MA - 0))) {
			new_state = 4;
		}
		else if ((old_state >= 4 && a <= (THRESHOLD_1500MA - THRESHOLD_HYSTERISIS)) || (old_state == 3 && a > (THRESHOLD_750MA - THRESHOLD_HYSTERISIS)) || (old_state == 2 && a > (THRESHOLD_750MA + THRESHOLD_HYSTERISIS)) || (old_state < 2 && a > (THRESHOLD_750MA + 0))) {
			new_state = 3;
		}
		else if ((old_state >= 3 && a <= (THRESHOLD_750MA - THRESHOLD_HYSTERISIS)) || (old_state == 2 && a > (THRESHOLD_250MA - THRESHOLD_HYSTERISIS)) || (old_state == 1 && a > (THRESHOLD_250MA + THRESHOLD_HYSTERISIS)) || (old_state < 1 && a > (THRESHOLD_250MA + 0))) {
			new_state = 2;
		}
		else if ((old_state >= 2 && a <= (THRESHOLD_250MA - THRESHOLD_HYSTERISIS)) || (old_state == 1 && a > (THRESHOLD_MIN - THRESHOLD_HYSTERISIS)) || (old_state == 0 && a > (THRESHOLD_MIN + THRESHOLD_HYSTERISIS))) {
			new_state = 1;
		}
		
		last_state[i] = new_state;
		
		if (new_state == 0)
		{
			// turn off LEDs
			LED_COMMON_PORTx = 0;
			LED_COLOUR_DDRx = 0;
		}
		else
		{
			// activate particular colour for particular index on LED matrix
			LED_COMMON_PORTx = _BV(i);
			if (new_state == 1) {
				LED_COLOUR_DDRx = _BV(LED_BLUE_BIT);
				LED_COLOUR_PORTx = LED_ALL_BITS & (~_BV(LED_BLUE_BIT));
			}
			else if (new_state == 2) {
				LED_COLOUR_DDRx = _BV(LED_GREEN_BIT);
				LED_COLOUR_PORTx = LED_ALL_BITS & (~_BV(LED_GREEN_BIT));
			}
			else if (new_state == 3) {
				LED_COLOUR_DDRx = _BV(LED_RED_BIT);
				LED_COLOUR_PORTx = LED_ALL_BITS & (~_BV(LED_RED_BIT));
			}
			else if (new_state >= 4) {
				LED_COLOUR_DDRx = _BV(LED_BLUE_BIT) | _BV(LED_RED_BIT);
				LED_COLOUR_PORTx = LED_ALL_BITS & (~(_BV(LED_BLUE_BIT) | _BV(LED_RED_BIT)));
			}
		}
	}
}

#ifndef ARDUINO
int main(void)
{
    setup();
	while(1) {
		loop();
	}
}
#endif

(note: use factory default fuse-bit settings from the datasheet, not Arduino default fuse-bit settings)

This project is inspired by Adafruit’s USB current gauge.

I have one but I prefer to use a red-green-blue LED because they are easier to read from far away compared to a bargraph (it’s easier for your eyes to distinguish different colours than it is to count tiny LEDs from far away). Plus I also needed 6 of these. If you are lazy, just buy 6 of these and connect them together with a beefy power supply and you are good to go.

21 thoughts on “Simple 6X USB Charger with Current Monitor

  1. Ross

    Awesome project! You have inspired me to build something similar using an msp430, and a different DC/DC converter (just whatever I have in my box).

    That Murata device is impressive, 10A for $10!

    Reply
    1. Matthew

      There’s even a GE one that’s similar in specs but shows up slightly cheaper, though I haven’t compared the efficiency at Vout=5V. I’d imagine the author did, though.

      GE NQR010A0X4Z

      Reply
  2. BurhanMir

    Please explain this part “(note: use factory default fuse-bit settings from the datasheet, not Arduino default fuse-bit settings)”

    I am actually a bit amature but i really want to make this thing. Hope you help. Thank you

    Reply
    1. Admin Post author

      The default low fuse bits are supposed to be 0x62 and the high fuse bits are supposed to be 0xD9. This means the AVR will run at 1MHz with the internal RC oscillator and will not use the bootloader on reset.

      Arduino chips are reprogrammed with low fuse bits of 0xFF and high fuse bits of 0xDE, which means the AVR will run at 16MHz with an external crystal and will run a bootloader on reset. You do not want this behaviour.

      Reply
  3. Matthew

    Nice! I had been working on something pretty much identical, but with an LCD display. I kept thinking it was a bit overdesigned. This is great!

    Reply
    1. Admin Post author

      You can achieve a greater level of detail with a LCD, you can actually display the exact milliamps being drawn. Good luck and have fun with your project.

      Reply
  4. vlad

    where/how did you find the values for the resistors on the D-/D+ lines ? cause i tried to make something similar (but battery powered) and it did not connect the D-/D+ lines to anything and the charger only works on older phones

    Reply
  5. Ross

    If you are not charging an iPad, you can get away with simply shorting D+ and D- together. The top three ports in the underside pictures have the data pins shorted and would work great for android phones and maybe even iphones? but probably not iPads. I haven’t worked with apple devices, but I know the shorting has worked for my android phones.

    Reply
    1. Admin Post author

      Right, as you see, I did try that, and it works with Android phones, but for some reason, my Sony wireless headset refuses to charge on the ports with D+ and D- shorted.

      Reply
  6. Chas

    I also have been inspired to build one myself based on a PIC. Nice post.. I like that INA169.. I’ve been using something that requires a lot more board real-estate. Thanks for sharing.

    Reply
  7. Olivier

    I’d like to Start with SMD Soldering, Could You Tell me the size of resistors in mm used. Any good Source for tutorials with SMD on Strip/Perfboards? Regards & respect, your colorcoding is really cool.

    Reply
    1. Admin Post author

      “0805” and “0603” in imperial are “2012” and “1608” in metric. Both work on perfboards. You don’t need a tutorial, these are easier to solder than through-hole because the 0805 pad is bigger than a pin, you just need tweezers.

      Reply
  8. Mbanzi

    I get a compilation error with the code provided. In the definition of adc_read() on line 29, there are no parameters but you call the function on line 52 with a parameter: “adc_read(i);”

    Is there an up to date version of the code? I removed the parameter in the adc_read call, everything seems to work ok but I notice the LED flickers for example green & red when charging at 1A.

    Thanks, great project idea!

    Reply
    1. Admin Post author

      There is no update to the code. I can’t update it right now because I don’t have my equipment to test with.

      The flickering is probably just because the current is right in between two zones, and the hysteresis isn’t enough. Adjust the constants and see what happens, or add a LPF digital filter to the code. Or the simplest way is to just slow down the sampling loop.

      I was going to slow down the sampling loop but I ended up preferring the flickering, it’s how I know that the current is in between two zones.

      Reply
  9. Mike Sims

    I had a theory that I recent;ly verified using an app on my iPad. The theory is that when an iPad is plugged into a charger while it is in use, and if the user is running hardware intensive applications such as Real Racing 3 or some other demanding graphics app, the iPad will actually consume amperage HIGHER than 2.1 amps so it can maintain charging during this kind of use. When I leave my iPad plugged in while running apps like this, I actually see the charge going down as opposed to going up (at a MUCH slower rate than if it were not plugged in of course).

    Here’s my question … will this circuit supply MORE than 2.1 amps to a device if the device wants more amperage?

    I’ve been trying to build a 3 amp charger for my iPad without success. I took electronics back in 1991, but then became a network engineer, so my knowledge of electronics is no where near as good as it use to be. I feel like an idiot sometimes honestly… Any advice would be much appreciated. my email is: sims.mike@ gmail.com

    Reply
    1. Admin Post author

      I used a OKR-T/10-W12, which should be able to supply 10A, which is shared between all devices. So if one device takes 3A, then the other 5 devices can take 7A more.

      The current sensor should be fine also.

      I have not tested this, you might want to test it while monitoring the temperature of the OKR-T/10-W12, if it gets hot, add a heatsink. It should have its own internal temperature safety cutoff too.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>