I noticed an annoying characteristic of the device. The device will not run on battery unless the battery has been charged a little bit. Even then, it will shut off after about 30 seconds due to the low power inactivity shut off function in the IP-5306. This is why power banks shut off on makers trying to run microcontrollers from them.
In my conversations with Grok, it suggested two ways to deal with this. One was to run a 1K resistor from Pin 5 (Key) of the IP-5306 to a GPIO pin and pull the pin down every 20 seconds or so to simulate a button press.
Bodge resistor #1
Well I ran a 1.2K resistor to GPIO3 resistor and with the following code, my board will start working after plugging in the Supermini board and then unplugging it. It pulled battery power and ran. It also did not shut off. Ran it for about an hour and saw a 0.2V drop in the battery.
from machine import Pin
import time
# GPIO pin to control the KEY pin of the IP5306 (GPIO 3)
key_pin = Pin(3, Pin.OUT)
# Function to simulate a short press on the KEY pin (50ms to 2s)
def simulate_short_press():
print("Simulating short press on KEY pin to enable/keep boost output on...")
key_pin.value(0) # Pull KEY pin low
time.sleep(0.1) # Hold for 100ms
key_pin.value(1) # Release the KEY pin
print("Short press completed.")
# Initialize the KEY pin to high (inactive)
key_pin.value(1)
# Wait a moment to ensure the ESP32C6 is fully powered
time.sleep(1)
# Simulate an initial short press to enable the boost output
simulate_short_press()
# Main loop to periodically simulate short presses and keep the system running
while True:
# Simulate a short press every 20 seconds to ensure the boost output stays on
simulate_short_press()
print("System is running...")
time.sleep(20) # Repeat every 20 seconds
I also tried the I2C pins input that should have worked. However, the only I2C device that came up when I did a sweep was the accelerometer. I tried swapping the SCL and SDA pins to see if I got the order reversed by to no avail.
Well I got some learnings! Chat GPT suggested that the I2C part might have to be turned on. It wrote a little skit to simulated the button press on the Key pin and then scanned for I2C addresses and all of a sudden we got an 0x75 address. Here was the code.
import machine, time
# Simulate a button press on the KEY pin via GPIO3.
key_pin = machine.Pin(3, machine.Pin.OUT)
# Ensure KEY is high by default (if not, adjust logic accordingly)
key_pin.value(1)
time.sleep_ms(10) # a brief pause
# Pull KEY low to simulate a short press (active low assumed)
key_pin.value(0)
time.sleep_ms(2000) # hold low for 50ms (adjust as needed)
key_pin.value(1)
time.sleep_ms(50)
# Optionally, set the pin back to input mode
key_pin.init(machine.Pin.IN)
i2c = machine.I2C(0, scl=machine.Pin(19), sda=machine.Pin(20), freq=400000)
devices = i2c.scan()
print("I2C freaky devices found:", devices)
So the next job was to manipulate some registers to limit the current to 200 mA for charging because I am using a 200 mAh battery and to remove the low current shut off "feature". This is called Boost always on. I needed to modify the time to pull the Key low from 50ms to 500ms to make it work.
import machine
import time
# --- 1. Simulate KEY press on GPIO3 to enable I2C mode on the IP5306 ---
key_pin = machine.Pin(3, machine.Pin.OUT)
key_pin.value(1) # Ensure KEY is high by default.
time.sleep_ms(10) # Brief pause.
key_pin.value(0) # Pull KEY low to simulate a press.
time.sleep_ms(500) # Hold low for 50ms (adjust if needed).
key_pin.value(1) # Release KEY.
time.sleep_ms(50)
key_pin.init(machine.Pin.IN) # Return KEY pin to input.
# --- 2. Initialize I2C on GPIO19 (SCL) and GPIO20 (SDA) at 400KHz ---
i2c = machine.I2C(0, scl=machine.Pin(19), sda=machine.Pin(20), freq=400000)
print("I2C devices found:", i2c.scan()) # Expect: [14, 117]
# --- 3. Define the IP5306 class with helper methods ---
# (We use address 0x75 for the IP5306, as discovered.)
IP5306_I2C_ADDR = 0x75
class IP5306:
def __init__(self, i2c, addr=IP5306_I2C_ADDR):
self.i2c = i2c
self.addr = addr
def read_reg(self, reg):
"""Read one byte from register 'reg'."""
return self.i2c.readfrom_mem(self.addr, reg, 1)[0]
def write_reg(self, reg, val):
"""Write one byte 'val' to register 'reg'."""
self.i2c.writeto_mem(self.addr, reg, bytes([val]))
def update_reg(self, reg, mask, value):
"""
Read-modify-write: update only the bits specified by mask.
"""
cur = self.read_reg(reg)
new_val = (cur & ~mask) | (value & mask)
self.write_reg(reg, new_val)
def dump_registers(self, start=0x00, end=0x30):
"""Dump registers for debugging."""
print("IP5306 Register Dump:")
for reg in range(start, end):
val = self.read_reg(reg)
print(" Reg 0x{:02X} = 0x{:02X}".format(reg, val))
def enable_boost_always_on(self):
"""
Enable BOOST always-on by setting SYS_CTL0 (0x00) bit1.
"""
self.update_reg(0x00, 0x02, 0x02)
print("BOOST always-on enabled.")
def get_charging_status(self):
"""
Read charging status:
- REG_READ0 (0x70) bit3: charging enable flag.
- REG_READ1 (0x71) bit3: full-charge flag.
"""
reg70 = self.read_reg(0x70)
reg71 = self.read_reg(0x71)
charging = bool(reg70 & (1 << 3))
full = bool(reg71 & (1 << 3))
return {"charging": charging, "full": full}
def set_charge_current_detection(self, current_setting):
"""
Adjust charging current detection via Charger_CTL1 (0x21), bits [7:6]:
0 -> 200mA, 1 -> 400mA, 2 -> 500mA, 3 -> 600mA.
For a 200 mA battery, use setting 0.
"""
if current_setting not in (0, 1, 2, 3):
print("Invalid current setting!")
return
self.update_reg(0x21, 0xC0, current_setting << 6)
print("Charge current detection set to", current_setting, "(0=200mA)")
def disable_low_power_shutdown(self):
"""
Disable the low power shutdown by clearing bit0 in SYS_CTL1 (0x01).
"""
self.update_reg(0x01, 0x01, 0x00)
print("Low power shutdown disabled.")
# --- 4. Create an IP5306 instance and adjust parameters ---
ip5306 = IP5306(i2c)
print("Dumping registers for debugging:")
ip5306.dump_registers()
ip5306.enable_boost_always_on()
ip5306.set_charge_current_detection(0) # Set for 200 mA.
ip5306.disable_low_power_shutdown()
status = ip5306.get_charging_status()
print("Initial charging status:", status)
print("IP5306 setup complete.")
# --- 5. Blink an external LED ---
# Since the built-in LED pins (6 or 7) are controlled by the IP5306,
# use a free GPIO pin for an external LED. Here, we use GPIO12.
external_led = machine.Pin(15, machine.Pin.OUT)
external_led.value(0) # Start with LED off.
print("Blinking external LED on GPIO15...")
while True:
external_led.value(1)
time.sleep(0.5)
external_led.value(0)
time.sleep(0.5)
The output to this is
MPY: soft reboot
I2C devices found: [14, 117]
Dumping registers for debugging:
IP5306 Register Dump:
Reg 0x00 = 0x37
Reg 0x01 = 0x1C
Reg 0x02 = 0x64
Reg 0x03 = 0x63
Reg 0x04 = 0x68
Reg 0x05 = 0x00
Reg 0x06 = 0x02
Reg 0x07 = 0xB0
Reg 0x08 = 0xA2
Reg 0x09 = 0xDE
Reg 0x0A = 0x2C
Reg 0x0B = 0x1B
Reg 0x0C = 0x64
Reg 0x0D = 0xAC
Reg 0x0E = 0x00
Reg 0x0F = 0x06
Reg 0x10 = 0xBD
Reg 0x11 = 0x7E
Reg 0x12 = 0xD8
Reg 0x13 = 0x92
Reg 0x14 = 0x80
Reg 0x15 = 0x78
Reg 0x16 = 0x00
Reg 0x17 = 0x00
Reg 0x18 = 0x00
Reg 0x19 = 0x00
Reg 0x1A = 0x00
Reg 0x1B = 0x00
Reg 0x1C = 0x00
Reg 0x1D = 0x00
Reg 0x1E = 0x00
Reg 0x1F = 0x00
Reg 0x20 = 0x01
Reg 0x21 = 0x09
Reg 0x22 = 0x02
Reg 0x23 = 0xBF
Reg 0x24 = 0xD6
Reg 0x25 = 0x00
Reg 0x26 = 0x00
Reg 0x27 = 0x00
Reg 0x28 = 0x00
Reg 0x29 = 0x00
Reg 0x2A = 0x00
Reg 0x2B = 0x00
Reg 0x2C = 0x00
Reg 0x2D = 0x00
Reg 0x2E = 0x00
Reg 0x2F = 0x00
BOOST always-on enabled.
Charge current detection set to 0 (0=200mA)
Low power shutdown disabled.
Initial charging status: {'charging': False, 'full': False}
IP5306 setup complete.
Blinking external LED on GPIO15...
After running this, I can remove the USB from the ESP32C6 and the blue led on board the ESP32 will keep blinking.
So what did I learn.
1) Link the Key pin (5) with a GPIO via a 1.2K resistor.
2) Bring this pin LOW for 500ms to turn on the I2C part of the IP-5306I2C.
3) Once we got past the messiness of turning on the I2C, ChatGPT did real
well to work with the registers.
Here is a Chat GPT translation of the I2C portion of the IP5306 12C datasheet.
14. IP5306 I²C Register Documentation
The datasheet includes a section titled “IP5306 寄存器文档” which details the register map and describes the function of each register. Key points include:
14.1 I²C Protocol
- Data Format:
- I²C operates at up to 400 kbps.
- Uses 8-bit addressing and 8-bit data.
- Data is transmitted MSB first.
- Write Operation Example:
- To write 0x5A to register 0x05 at slave address 0xEA (8-bit), the master sends:
- Start → Slave address (0xEA) → ACK from slave → Register address (0x05) → ACK → Data (0x5A) → ACK → Stop.
- To write 0x5A to register 0x05 at slave address 0xEA (8-bit), the master sends:
- Read Operation Example:
- To read from register 0x05, the master first writes the register address then sends a repeated start and reads the data.
14.2 I²C Application Considerations
- The I²C interface must be enabled (by default, the standard IP5306 does not support I²C unless it is the I²C-enabled version).
- When modifying a register, always use a read–modify–write approach to change only the desired bits.
- It is recommended to use the IRQ (interrupt) signal to determine whether the IP5306 is in working or standby mode:
- IRQ = High: Working
- IRQ = High-Impedance: Standby
- Specific register bits:
- Register 0x70 (REG_READ0): Bit 3 indicates whether charging is enabled (1 = charging on).
- Register 0x71 (REG_READ1): Bit 3 indicates battery full status (1 = battery full).
14.3 Register Addresses and Descriptions
- SYS_CTL0 (Address 0x00):
- Bit 5: Boost enable (0 = disable, 1 = enable)
- Bit 4: Charger enable (0 = disable, 1 = enable)
- Bit 2: Enables automatic power-on when a load is inserted (0 = disable, 1 = enable)
- Bit 1: BOOST output always-on function (0 = disable, 1 = enable)
- Bit 0: Key shutdown function (0 = disable, 1 = enable)
- SYS_CTL1 (Address 0x01):
- Bit 7: Controls the selection of the boost control signal (1 = long press, 0 = double short press)
- Bit 6: Controls the selection of the WLED (flashlight) signal (1 = double short press, 0 = long press)
- Bit 5: Short press to switch boost (0 = disable, 1 = enable)
- Bits 4–3: Reserved
- Bit 2: Determines whether boost output is enabled when VIN is disconnected (0 = off, 1 = on)
- Bit 0: Batlow 3.0V low-power shutdown enable (0 = disable, 1 = enable)
- SYS_CTL2 (Address 0x02):
- Bits 3–2: Light-load shutdown time settings (with values corresponding to 8, 16, 32, or 64 seconds)
- Bits 1–0: Reserved
- Charger_CTL0 (Address 0x20):
- Bits 1–0: Charging termination voltage setting (with values corresponding to different cutoff voltages, e.g., 4.2V, 4.3V, 4.35V, 4.4V)
- Charger_CTL1 (Address 0x21):
- Bits 7–6: Battery-side charging current detection setting (e.g., 11 for 600 mA, 10 for 500 mA, 01 for 400 mA, 00 for 200 mA)
- Bits 4–2: Charging undervoltage threshold setting during charging (affecting the output voltage during charging)
- Bit 1–0: Charging constant voltage step-up setting
- Additional registers (REG_READ0, REG_READ1, REG_READ2, REG_READ3):
- Provide status information such as charging enable flag, full-charge flag, light-load flag, and key press flags (double click, long press, short press).
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.