To test-drive the I2C interface on the PB0A board, you just need a microcontroller, and a few wires and resistors.
Overview
For communication, both PB0A and the microcontroller must either run from the same power source, or use a special level shifter.
Since PB0A is powered from the battery input (range 3.0-4.2V), normally it is not possible to run your microcontroller from this same voltage source. Most microcontrollers are not compatible with this voltage range and would be damaged.
Before we look at the requirements for a suitable level shifter that would allow your microcontroller to be run from a different voltage source, there is one simple way for a microcontroller to communicate with the PB0A:
Just power PB0A from the 3.3V
pin of your microcontroller, simulating a battery at this level.
Ensuring Compatible Battery Voltage
Simply connect 3.3V
of your microcontroller to BAT+
on PB0A, and connect GND
of both devices.
This is a test setup simply to test-drive and experiment with the I2C interface. Obviously, the 3.3V
output of your microcontroller is not strong enough to drive external loads. So make sure there are no external loads connected to your PB0A’s 5V+
and 5V-
while powering it from your microcontroller pin.
I2C Interface Pins
Here is how PB0A exposes its I2C interface:
Pin | Pin on PCB | Description |
---|---|---|
SDA | DATA |
I2C data line |
SCL/SCK | unmarked surface pad | I2C clock |
VDD | BAT+ |
I2C is using battery voltage level |
GND | GND |
I2C and external microcontroller must share GND |
IRQ | unmarked surface pad | high when IP5306 power output is active |
Wiring
Three resistors are required:
- Pull-Up:
BothSDA
andSCL
must be pulled up by a strong external pull-up resistor (2.2KOhm or lower). It is not sufficient to use internal pull-up resistors of your microcontroller. - Pull-Down:
IRQ must be pulled down by a typical 10KOhm resistor if you need the IRQ line. This line ishigh
when the PB0A power output is active, elselow
.
Source Code
In my test setup I used a ESP32S DevKit V4, and I used this platform.ini:
[env:esp32dev]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
board_upload.flash_size = 4MB
monitor_speed = 115200
upload_speed = 921600
build_flags =
#-DBOARD_HAS_PSRAM
# -mfix-esp32-psram-cache-issue
-DARDUINO_ESP32_DEV
Any microcontroller will do. If you decide to use a different one, adjust GPIO numbers in wiring and source code as appropriate.
Main Program
The main program uses the wire library to establish the I2C communication:
#include <Arduino.h>
#include <Wire.h>
#include <ip5306.h>
#define IRQ1 23 // GPIO for IRQ input
#define I2C = 117 // IP5306 I2C address
void setup()
{
pinMode(IRQ1, INPUT);
Wire.begin();
Serial.begin(115200);
delay(1000);
}
void loop()
{
bool active;
byte error, address;
Serial.print("0x75: ");
bool active = digitalRead(IRQ1);
bool i2c_ready = false;
if (active)
{
Wire.beginTransmission(I2C);
error = Wire.endTransmission();
i2c_ready = (error == 0);
}
if (i2c_ready) {
Serial.println("OK");
bool lpt = IP5306_GetLongPressTime();
Serial.print("Long Press Time: ");
Serial.println(lpt);
bool pws = IP5306_GetPowerSource();
Serial.print("Power Source: ");
Serial.println(pws);
bool lld = IP5306_GetOutputLoad();
Serial.print("Light Load: ");
Serial.println(lld);
int llst = IP5306_GetOutputLoad();
Serial.print("Light Load shutdown time: ");
Serial.println(llst);
int vin = IP5306_GetVinCurrent();
Serial.print("Vin Current: ");
Serial.println(vin);
} else {
Serial.println("---");
}
delay(500); // wait 5 seconds for next scan
}
It uses a special IP5306 library to handle the specific I2C registers and interpret the meaning of register bits.
IP5306 Library
Either use one of the many available IP5306 libraries, or add the library to your project yourself:
- In platform.io, in your project, create a new folder named
IP5306
in the existing subfolderlib
. - In the folder
IP5306
, create two files:ip5306.h
andip5306.cpp
.
Source code for ip5306.h
Here is the source code for ip5306.h
:
/*
IP5306.h - Library for IP5306_I2C Power controller.
Created by Sebastian Haap, December 3, 2019.
Based on https://gist.github.com/me-no-dev/7702f08dd578de5efa47caf322250b57
*/
#ifndef IP5306_h
#define IP5306_h
#include "Wire.h"
#define IP5306_REG_SYS_0 0x00
#define IP5306_REG_SYS_1 0x01
#define IP5306_REG_SYS_2 0x02
#define IP5306_REG_CHG_0 0x20
#define IP5306_REG_CHG_1 0x21
#define IP5306_REG_CHG_2 0x22
#define IP5306_REG_CHG_3 0x23
#define IP5306_REG_CHG_4 0x24
#define IP5306_REG_READ_0 0x70
#define IP5306_REG_READ_1 0x71
#define IP5306_REG_READ_2 0x72
#define IP5306_REG_READ_3 0x77
#define IP5306_REG_READ_4 0x78
#define IP5306_GetKeyOffEnabled() ip5306_get_bits(IP5306_REG_SYS_0, 0, 1)
#define IP5306_SetKeyOffEnabled(v) ip5306_set_bits(IP5306_REG_SYS_0, 0, 1, v)
//0:dis,*1:en
#define IP5306_GetBoostOutputEnabled() ip5306_get_bits(IP5306_REG_SYS_0, 1, 1)
#define IP5306_SetBoostOutputEnabled(v) ip5306_set_bits(IP5306_REG_SYS_0, 1, 1, v)
//*0:dis,1:en
#define IP5306_GetPowerOnLoadEnabled() ip5306_get_bits(IP5306_REG_SYS_0, 2, 1)
#define IP5306_SetPowerOnLoadEnabled(v) ip5306_set_bits(IP5306_REG_SYS_0, 2, 1, v)
//0:dis,*1:en
#define IP5306_GetChargerEnabled() ip5306_get_bits(IP5306_REG_SYS_0, 4, 1)
#define IP5306_SetChargerEnabled(v) ip5306_set_bits(IP5306_REG_SYS_0, 4, 1, v)
//0:dis,*1:en
#define IP5306_GetBoostEnabled() ip5306_get_bits(IP5306_REG_SYS_0, 5, 1)
#define IP5306_SetBoostEnabled(v) ip5306_set_bits(IP5306_REG_SYS_0, 5, 1, v)
//0:dis,*1:en
#define IP5306_GetLowBatShutdownEnable() ip5306_get_bits(IP5306_REG_SYS_1, 0, 1)
#define IP5306_SetLowBatShutdownEnable(v) ip5306_set_bits(IP5306_REG_SYS_1, 0, 1, v)
//0:dis,*1:en
#define IP5306_GetBoostAfterVin() ip5306_get_bits(IP5306_REG_SYS_1, 2, 1)
#define IP5306_SetBoostAfterVin(v) ip5306_set_bits(IP5306_REG_SYS_1, 2, 1, v)
//0:Closed, *1:Open
#define IP5306_GetShortPressBoostSwitchEnable() ip5306_get_bits(IP5306_REG_SYS_1, 5, 1)
#define IP5306_SetShortPressBoostSwitchEnable(v) ip5306_set_bits(IP5306_REG_SYS_1, 5, 1, v)
//*0:disabled, 1:enabled
#define IP5306_GetFlashlightClicks() ip5306_get_bits(IP5306_REG_SYS_1, 6, 1)
#define IP5306_SetFlashlightClicks(v) ip5306_set_bits(IP5306_REG_SYS_1, 6, 1, v)
//*0:short press twice, 1:long press
#define IP5306_GetBoostOffClicks() ip5306_get_bits(IP5306_REG_SYS_1, 7, 1)
#define IP5306_SetBoostOffClicks(v) ip5306_set_bits(IP5306_REG_SYS_1, 7, 1, v)
//*0:long press, 1:short press twice
#define IP5306_GetLightLoadShutdownTime() ip5306_get_bits(IP5306_REG_SYS_2, 2, 2)
#define IP5306_SetLightLoadShutdownTime(v) ip5306_set_bits(IP5306_REG_SYS_2, 2, 2, v)
//0:8s, *1:32s, 2:16s, 3:64s
#define IP5306_GetLongPressTime() ip5306_get_bits(IP5306_REG_SYS_2, 4, 1)
#define IP5306_SetLongPressTime(v) ip5306_set_bits(IP5306_REG_SYS_2, 4, 1, v)
//*0:2s, 1:3s
#define IP5306_GetChargingFullStopVoltage() ip5306_get_bits(IP5306_REG_CHG_0, 0, 2)
#define IP5306_SetChargingFullStopVoltage(v) ip5306_set_bits(IP5306_REG_CHG_0, 0, 2, v)
//0:4.14V, *1:4.17V, 2:4.185V, 3:4.2V (values are for charge cutoff voltage 4.2V, 0 or 1 is recommended)
#define IP5306_GetChargeUnderVoltageLoop() ip5306_get_bits(IP5306_REG_CHG_1, 2, 3)
//Automatically adjust the charging current when the voltage of VOUT is greater than the set value
#define IP5306_SetChargeUnderVoltageLoop(v) ip5306_set_bits(IP5306_REG_CHG_1, 2, 3, v)//Vout=4.45V + (v * 0.05V) (default 4.55V)
// //When charging at the maximum current, the charge is less than the set value. Slowly reducing the charging current to maintain this voltage
#define IP5306_GetEndChargeCurrentDetection() ip5306_get_bits(IP5306_REG_CHG_1, 6, 2)
#define IP5306_SetEndChargeCurrentDetection(v) ip5306_set_bits(IP5306_REG_CHG_1, 6, 2, v)
//0:200mA, 1:400mA, *2:500mA, 3:600mA
#define IP5306_GetVoltagePressure() ip5306_get_bits(IP5306_REG_CHG_2, 0, 2)
#define IP5306_SetVoltagePressure(v) ip5306_set_bits(IP5306_REG_CHG_2, 0, 2, v)
//0:none, 1:14mV, *2:28mV, 3:42mV (28mV recommended for 4.2V)
#define IP5306_GetChargeCutoffVoltage() ip5306_get_bits(IP5306_REG_CHG_2, 2, 2)
#define IP5306_SetChargeCutoffVoltage(v) ip5306_set_bits(IP5306_REG_CHG_2, 2, 2, v)
//*0:4.2V, 1:4.3V, 2:4.35V, 3:4.4V
#define IP5306_GetChargeCCLoop() ip5306_get_bits(IP5306_REG_CHG_3, 5, 1)
#define IP5306_SetChargeCCLoop(v) ip5306_set_bits(IP5306_REG_CHG_3, 5, 1, v)
//0:BAT, *1:VIN
#define IP5306_GetVinCurrent() ip5306_get_bits(IP5306_REG_CHG_4, 0, 5)
#define IP5306_SetVinCurrent(v) ip5306_set_bits(IP5306_REG_CHG_4, 0, 5, v)
// Charging current: I=0.05 + 00.1 + 10.2 + 20.4 + 30.8 + 4*1.6A
#define IP5306_GetShortPressDetected() ip5306_get_bits(IP5306_REG_READ_3, 0, 1)
#define IP5306_ClearShortPressDetected() ip5306_set_bits(IP5306_REG_READ_3, 0, 1, 1)
#define IP5306_GetLongPressDetected() ip5306_get_bits(IP5306_REG_READ_3, 1, 1)
#define IP5306_ClearLongPressDetected() ip5306_set_bits(IP5306_REG_READ_3, 1, 1, 1)
#define IP5306_GetDoubleClickDetected() ip5306_get_bits(IP5306_REG_READ_3, 2, 1)
#define IP5306_ClearDoubleClickDetected() ip5306_set_bits(IP5306_REG_READ_3, 2, 1, 1)
#define IP5306_GetPowerSource() ip5306_get_bits(IP5306_REG_READ_0, 3, 1)
//0:BAT, 1:VIN
#define IP5306_GetBatteryFull() ip5306_get_bits(IP5306_REG_READ_1, 3, 1)
//0:CHG/DIS, 1:FULL
#define IP5306_GetLevelLeds() ((~ip5306_get_bits(IP5306_REG_READ_4, 4, 4)) & 0x0F)
//LED[0-4] State (inverted)
#define IP5306_GetOutputLoad() ip5306_get_bits(IP5306_REG_READ_2, 2, 1)
//0:heavy, 1:light
#define IP5306_LEDS2PCT(byte) \
((byte & 0x01 ? 25 : 0) + \
(byte & 0x02 ? 25 : 0) + \
(byte & 0x04 ? 25 : 0) + \
(byte & 0x08 ? 25 : 0))
int ip5306_get_reg(uint8_t reg);
int ip5306_set_reg(uint8_t reg, uint8_t value);
uint8_t ip5306_get_bits(uint8_t reg, uint8_t index, uint8_t bits);
void ip5306_set_bits(uint8_t reg, uint8_t index, uint8_t bits, uint8_t value);
#endif
Source Code for ip5306.cpp
And here is the source code for ip5306.cpp
:
/*
IP5306.cpp - Library for IP5306_I2C Power controller.
Created by Sebastian Haap, December 3, 2019.
Based on https://gist.github.com/me-no-dev/7702f08dd578de5efa47caf322250b57
*/
#include "Wire.h"
#include "ip5306.h"
int ip5306_get_reg(uint8_t reg){
Wire.beginTransmission(0x75);
Wire.write(reg);
if(Wire.endTransmission(false) == 0 && Wire.requestFrom(0x75, 1)){
return Wire.read();
}
return -1;
}
int ip5306_set_reg(uint8_t reg, uint8_t value){
Wire.beginTransmission(0x75);
Wire.write(reg);
Wire.write(value);
if(Wire.endTransmission(true) == 0){
return 0;
}
return -1;
}
uint8_t ip5306_get_bits(uint8_t reg, uint8_t index, uint8_t bits){
int value = ip5306_get_reg(reg);
if(value < 0){
//Serial.printf("ip5306_get_bits fail: 0x%02x\n", reg);
return 0;
}
return (value >> index) & ((1 << bits)-1);
}
void ip5306_set_bits(uint8_t reg, uint8_t index, uint8_t bits, uint8_t value){
uint8_t mask = (1 << bits) - 1;
int v = ip5306_get_reg(reg);
if(v < 0){
//Serial.printf("ip5306_get_reg fail: 0x%02x\n", reg);
return;
}
v &= ~(mask << index);
v |= ((value & mask) << index);
if(ip5306_set_reg(reg, v)){
//Serial.printf("ip5306_set_bits fail: 0x%02x\n", reg);
}
}
Running the Code
Once you uploaded the source code to your microcontroller, and then did the wiring, once the microcontroller is powered on (i.e. powered by its USB connector), PB0A also receives power.
The firmware establishes the I2C connection and dumps a selected number of information to the serial monitor.
Once I2C communication works, you can easily take a closer look at the ip5306
library and its methods. You’ll quickly realize which information now can be read and set by your microcontroller.
Keep in mind that IP5306/PB0A has no internal memory, and you cannot permanently change the settings. Whenever the PB0A enables its power output, the microcontroller must (re)configure PB0A via I2C.
Slow Website?
This website is very fast, and pages should appear instantly. If this site is slow for you, then your routing may be messed up, and this issue does not only affect done.land, but potentially a few other websites and downloads as well. Here are simple steps to speed up your Internet experience and fix issues with slow websites and downloads..
Comments
Please do leave comments below. I am using utteran.ce, an open-source and ad-free light-weight commenting system.
Here is how your comments are stored
Whenever you leave a comment, a new github issue is created on your behalf.
-
All comments become trackable issues in the Github Issues section, and I (and you) can follow up on them.
-
There is no third-party provider, no disrupting ads, and everything remains transparent inside github.
Github Users Yes, Spammers No
To keep spammers out and comments attributable, all you do is log in using your (free) github account and grant utteranc.es the permission to submit issues on your behalf.
If you don’t have a github account yet, go get yourself one - it’s free and simple.
If for any reason you do not feel comfortable with letting the commenting system submit issues for you, then visit Github Issues directly, i.e. by clicking the red button Submit Issue at the bottom of each page, and submit your issue manually. You control everything.
Discussions
For chit-chat and quick questions, feel free to visit and participate in Discussions. They work much like classic forums or bulletin boards. Just keep in mind: your valued input isn’t equally well trackable there.
(content created Jul 12, 2025)