The Pi Zero (and Zero W) are quite tolerant in respect to their power requirements. They can even be run from a LiFePO4 battery that has a voltage range of 3.6 to 2.5V. LiPo batteries are better-suited as their voltage ranges from 4.2 down to about 3V. When running the boards from the blank cell (it should have protection circuitry!), they last even longer because fewer losses occur since there is no boost converter in the system. In fact, the 5V is only needed for the USB host, although many devices are supposed to work from voltages down to 3V.
Unfortunately, these single board computers lack power management and don’t have shutdown buttons and there are not that many simple and cost-effective solutions for these problems. https://www.tindie.com/products/xorbit/lifepo4weredpi/ is one nice example, but unfortunately not fully open source.
My idea was to combine a MAX17043 LiPo fuel gauge with a pushbutton and a LiPo charging circuit to create a battery and safe shutdown management add-on for the Pi Zero.
The little board occupies only the first 10 pins and uses only GPIO4 and SDA + SCL. Design files for the tiny custom TP4056 charger board can be downloaded as a .zip archive:
The Python script the make the thing work is attached below. Add it to rc.local to execute on startup.
#!/usr/bin/python #Python Script for MAX17043 fuel gauge and safe shutdown button #in order to execute on startup add the following line to /etc/rc.local #(sleep 10;python /home/pi/fuelgauge.py) & #assuming that fuelgauge.py is in /home/pi #sleep 10 is probably not necessary import RPi.GPIO as GPIO import smbus import time import os #threshold in % below which the pi gets shut down threshold=15.0 buttonPin=4 # I2C-Adddress of MAX17043 address = 0x36 #registers VCELL_REGISTER=0x02 SOC_REGISTER=0x04 MODE_REGISTER=0x06 VERSION_REGISTER=0x08 CONFIG_REGISTER=0x0C COMMAND_REGISTER=0xFE # open the bus by creating an instance MAX17043 = smbus.SMBus(1) #set up GPIO for shutdown button GPIO.setmode(GPIO.BCM) #Using GPIO nr 4 GPIO.setup(buttonPin,GPIO.IN,pull_up_down=GPIO.PUD_UP) def reset(): MAX17043.write_byte_data(address, COMMAND_REGISTER, 0x00) MAX17043.write_byte_data(address, COMMAND_REGISTER, 0x04) def quickStart(): MAX17043.write_byte_data(address, MODE_REGISTER, 0x40) MAX17043.write_byte_data(address, MODE_REGISTER, 0x00) #get state of charge def getSOC(): # Konfiguration des MAX17043 MSB= MAX17043.read_byte_data(address, SOC_REGISTER) LSB = MAX17043.read_byte_data(address, SOC_REGISTER) percentage= MSB+ LSB/256.0 #print percentage if percentage < threshold: os.system("sudo shutdown -h now") #Arduino map function for convenience def arduino_map(x, in_min, in_max, out_min, out_max): return (x - in_min) * (out_max - out_min) // (in_max - in_min) + out_min #setup reset() quickStart() # Loop while True: getSOC() time.sleep(1.5) #shutdown if button is pressed and held if (GPIO.input(buttonPin)==0): os.system("sudo shutdown -h now") if __name__ == "__main__": main()
5 thoughts on “Raspberry Pi Zero Lipo Fuel Gauge With Safe Shutdown”
Does the reset work?
… COMMAND_REGISTER, 0x04
Should this not be x54?
um, good question, thanks for the hint. This has to be verified somehow. Since I’m resetting on startup, I have no clue whether it really works out. If you figure it out let me know.
Seems the instructions by BYTE dosn’t work on all registers.
Datasheet 17043/44: “Register reads and writes are only valid if all 16 bits are transferred.”
Datasheet 17048/49, same device, more sw-features: “All registers must be written and read as 16-bit words; 8-bit writes cause no effect.”
So the read and return of the SOC- or CONFIG-register by byte results twice times in LSB (0x05/0x0c). Datasheet SOC: “The upper byte least-significant bit has units of 1%. The lower byte provides additional resolution.”.
Same problem by writing by byte, instead use word. I can’t check if quickstart or reset happens. In your script there is no need to reset and quickstart at the same time.
Pls check your chip version nr:
print hex ( MAX17043.read_word_data ( address, 0x08 ) )
Mine shows 0x1200.
Hey, I tried “print hex ( MAX17043.read_word_data ( address, 0x08 ) )”, but got no output. Neither with read_byte_data, nor with read_word data.
Are chip versions that different? Annoying!
Yes, chip versions are different, mine must be a MAX17048 (can’t read the text on the chip) , yours a MAX17043. But there is no difference by accessing the version register. Yours shoult show a “3” or 0x0300, mine shows “18” or 0x1200 which “can” be the chiptype MAX1704(8). But I’m not shure.
MAX1704(3), MAX1704(4), MAX1704(8), MAX1704(9)
# tested on a RPi 2
MAX17043 = smbus.SMBus(1)
print hex ( MAX17043.read_word_data ( 0x36, 0x08 ) )