EP-0245: Difference between revisions

From 52Pi Wiki
Jump to navigation Jump to search
Line 125: Line 125:
* 1 x Copper pillar pack
* 1 x Copper pillar pack
* 1 x Flat heat screws pack (M2.5)   
* 1 x Flat heat screws pack (M2.5)   
* 1 x UPS v6 instructions
* 1 x UPS Gen 6 instructions
[[File:EP-0245-PL.jpg|left|800px]]
[[File:EP-0245-PL.jpg|left|800px]]
<br style="clear:both;">
<br style="clear:both;">

Revision as of 17:58, 13 October 2025

UPS Gen 6 power module compatible with Raspberry Pi 5/4B

EP-0245-01.jpg

Description

The UPS Gen 6 is the latest version of the UPS Plus series, designed for enhanced power management on Raspberry Pi or other SBCs. It supports up to 4 channels of 8.4V lithium battery packs in parallel and can be expanded with two 3.7V 18650 batteries in series. The device features intelligent power path management, prioritizing external power and switching to battery power during outages. It also supports real - time monitoring of battery status via I2C and custom firmware uploads through DFU. Additional features include programmable LEDs, a PikaPython interface for advanced monitoring, auto - restart on power recovery, safe shutdown countdown, and low - battery loop protection. UPS Gen 6 offers a versatile solution for your projects and will gain more features through future OTA firmware upgrades.

Key Features

  • Bumpless Transfer
  • Hard Disk Power Supply Interface
  • Cooling System
  • OTA Expansion for Additional Functions
  • I2C Communication Interface
  • PikaPython Script Execution Capability
  • Physical Power Button

Specifications

  • Device Compatibility: Supports Raspberry Pi and Orange Pi series devices.
  • Expansion Battery Pack Support:Supports 18650 battery packs with two batteries in series, providing a typical voltage of 7.4V and a maximum voltage of 8.4V, with up to four parallel battery packs supported.
  • Power Management: Bumpless Transfer, Full UPS (Uninterruptible Power Supply) characteristics, supporting seamless failover to battery power.
  • Communication Interface: I2C protocol for battery and device status monitoring and controlling.
  • DIY Features: Programmable LED control via configuring i2c registers.
  • PikaPython support:PikaPython script interpreter for Python script injection.
  • Power Recovery Settings: Configurable voltage thresholds for auto-restart.
  • Safe shutdown timer: Saft shutdown timer to prevent data loss.
  • OTA Upgrade Features:Get more feature via OTA upgrade.

Compatibility Note

The screws, copper pillars, and other additional components of this product are primarily designed to fit the Raspberry Pi 5 and Raspberry Pi 4B. However, the product's functionality is compatible with other SBC (Single Board Computer) development boards on the market that are pin-compatible with the Raspberry Pi. If you need to install it smoothly, you may need to check whether the positioning hole locations match those of the Raspberry Pi 5 or Raspberry Pi 4B. Otherwise, it may not be physically compatible. This is hereby declared.

UPS features Details

Feature 1: Auto Start Mode

  • Register: auto_start_mode
  • Function: Enables automatic power-on logic when written.
  • Conditions for Auto Start: when Battery voltage > auto_start_voltage and Valid external power source present
  • Protection Mechanism: Monitors battery voltage after auto-start; If voltage drops to battery_protection_voltage multiple times:
   Auto-start attempts will be abandoned
  • Manual Shutdown by Button
   Auto clears auto_start_mode functionality

Feature 2: Delayed Shutdown

  • Register: shutdown_countdown
  Initiates shutdown after specified seconds when written.
  • Important Notes
  Combined with active auto_start_mode: Functions as a reboot if conditions are met
  For complete shutdown: Must clear auto_start_mode setting

Feature 3: Runtime Tracking

  • Register: runtime
  • Unit: milliseconds (ms)
  • Functionality: Tracks continuous operation time since last power-on

Feature 4: Force OTA Mode

  • Register: ota_request
  • Write value: 0xA5A5
  • Behavior: Immediately forces device into OTA mode

Feature 5: Physical Button Control

  • Current Implementation

Single button operation Toggles power state immediately

Important Notice

  • Immediate shutdown risks: Potential data loss
  • Recommended method: Use shutdown_countdown for safe shutdown

Feature 6: L_FAST LED Indicators

LED State Meaning
Fast blinking Bootloader mode
Solid on Fast charging available
Slow blinking UPS Fault
Off No input power

Gallery

  • Product Outlook
EP-0245-01.jpg


  • Back face
EP-0245-02.jpg


  • Assembly status
EP-0245-03.jpg


  • Port definitions
EP-0245-04.jpg


  • Easy to install
EP-0245-05.jpg


  • Heat dissipation effect
EP-0245-06.jpg


  • External Power supply
EP-0245-07.jpg


EP-0245-08.jpg


  • Standard Output for HDD (12V/5V)
EP-0245-09.jpg


Package List

  • 1 x Aluminum Heatsink with Automatically controlled Fan.
  • 1 x UPS Gen 6 mainboard
  • 1 x A series connect battery case
  • 1 x 4CM Battery Connector wire
  • 1 x Acrylic battery base frame
  • 1 x Metal battery base frame
  • 1 x Copper pillar pack
  • 1 x Flat heat screws pack (M2.5)
  • 1 x UPS Gen 6 instructions
EP-0245-PL.jpg


How to assemble it?

  • For Raspberry Pi 4B
EP-0245-IN-PI-4.jpg


  • For Raspberry Pi 5
EP-0245-IN-PI-5.jpg


YouTube Video

User Manual

  •  Please read the user manual carefully before proceeding.

This documentation assumes you are using a Raspberry Pi for testing. You can also use Orange Pi devices, but you will need to adjust the I2C controller address based on your specific setup. The default I2C device address for Raspberry Pi is /dev/i2c-1, while Orange Pi may use /dev/i2c-3. Adjust these settings according to your hardware.

The testing environment for this documentation includes:

  • Raspberry Pi 5
  • 4 battery packs, each consisting of two fully charged 18650 lithium batteries connected in series to provide 8.4V.
  • A 100W USB-C PD protocol charger.
  • Raspberry Pi OS 64-bit Bookworm, updated to the latest version.

If you are using a different operating system, refer to its specific environment configuration. Note that we currently provide configuration guidance only for Raspberry Pi OS. For other systems, please resolve compatibility issues independently.

Register Summary Table

Address Name Width Access Description Default Value
0x00 WHO_AM_I 8 R  Device identification register 0xA6
0x01 version 8 R Version number -
0x02 uuid0 32 R Unique ID (lower 32 bits) -
0x06 uuid1 32 R Unique ID (middle 32 bits) -
0x0A uuid2 32 R Unique ID (upper 32 bits) -
0x0E output_voltage 16 R Output voltage (unit: mV, 5V output) -
0x10 input_voltage 16 R Input voltage (unit: mV) -
0x12 battery_voltage 16 R Battery voltage (unit: mV) -
0x14 mcu_voltage 16 R MCU voltage (unit: mV) -
0x16 output_current 16 R Output current (unit: mA, 5V output) -
0x18 input_current 16 R Input current (unit: mA) -
0x1A battery_current 16 R Battery current (signed, unit: mA) -
0x1C temperature 8 R Temperature (two's complement, unit: °C) -
0x1D CR1 8 R/W Control Register 1 0x01
0x1E CR2 8 R/W Control Register 2 (Reserved) 0x00
0x1F SR1 8 R Status Register 1 -
0x20 SR2 8 R Status Register 2 -
0x21 battery_protection_voltage 16 R/W Battery protection voltage (unit: mV) 7400
0x23 shutdown_countdown 16 R/W Shutdown countdown (unit: seconds) -
0x25 auto_start_voltage 16 R/W Auto-start voltage threshold (unit: mV) 7400
0x27 rsv 16 R/W Python output buffer content length -
0x29 ota_request 16 W Writing to 0xA5A5 will request OTA mode. -
0x2B runtime 64 R Cumulative runtime (unit: microseconds) -
0x33 charge_detect_interval_s 16 R/W Charge detection interval (unit: seconds) -

Control Register CR1 (Address: 0x1D)

Bit Name Access Description Default Value
BIT0 auto_start_mode R/W Auto-start mode (0=disabled, 1=enabled) 1

Status Register SR1 (Address: 0x1F)

Bit Name Access Description Default Value
BIT0 sw_status R 5V output status (0=off, 1=on) -
BIT1 fast R Fast charging status (0=slow charging/no external power, 1=12V fast charging) -
BIT2 charge R Charge/discharge status (0=charging, 1=discharging) -
BIT3 input_low R Input voltage low (0=normal, 1=low) -
BIT4 output_low R 5V output low (0=normal, 1=low) -
BIT5 battery_low R Battery voltage low (0=normal, 1=low) -
BIT6 adc_mismatch R ADC mismatch (0=normal, 1=INA219 vs MCU sampling difference exceeded) -
BIT7 battery_fail R Battery failure (0=normal, 1=failure) -

Status Register SR2 (Address: 0x20)

Bit Name Access Description Default Value
BIT0 i2c_ack R/W LED on during I2C communication 1
BIT1 bat_charge R/W LED on during battery charging 0
BIT2 bat_discharge R/W LED on during battery discharging 0
BIT3 fault_report R/W LED on when fault detected 0
BIT4 ok_report R/W LED on during normal operation 0

Special Notes

  • Data Format:
  Voltage/current values are unsigned integers
  battery_current is signed (two's complement format)
  temperature is signed 8-bit integer (two's complement), range: -40°C to +85°C
  • Reserved Bits:
  All reserved bits default to 0
  Write operations to reserved bits are ignored (recommended to maintain default values)
  • The register definitions in the bootloader state are not fully compatible.

Getting Start

  • Assume that your Device Environment: Raspberry Pi 5, operating system: Raspberry Pi OS 64-bit (Bookworm).
  • Assume that your Raspberry Pi 5 can access internet including GitHub.
  • Assume that you have already assembled the UPS v6 with your Raspberry Pi 5 and booting up properly.
  • Please refer to the following steps for operation:

Install Dependencies package and libraries

  • Open a terminal and typing:
sudo apt update 
sudo apt upgrade -y 
sudo apt -y install build-essential cmake libi2c-dev libssl-dev gcc-arm-none-eabi git wget vim virtualenv i2c-tools

Download Demo codes repository

  • In order to use this module easily, I recommend you download the repository from GitHub, URL: [ https://github.com/geeekpi/upsv6_pub.git ]
  • Download to Raspberry Pi 5 locally, you need to open a terminal and typing following commands:
cd ~/
git clone https://github.com/geeekpi/upsv6_pub.git 

or just open a browser and access this URL: [ https://github.com/geeekpi/upsv6_pub.git ], click `code` -> click `Download ZIP` and then extract the contents to your Raspberry Pi 5 on desktop.

Downloadrepo.png



Upload new firmware

  • Step 1. Enter into OTA mode
  • Step 2. Upload new firmware_encrypted.bin file
python enable_ota.py 

Check if the UPS v6 has been entered into OTA mode:

 
i2cdetect -y 1 

If the i2c device address is changed from 0x17 to 0x18 means UPS Gen 6 is already in OTA mode.

./user_write_tool firmware_encrypted.bin 

Until you see following figure:

Flash complete.png


Push button status

  • Push button will keep the status after last operation.

How to read UPS status

  • Step 1. Download repository by using git command, open a terminal and typing following command:
cd ~
git clone https://github.com/geeekpi/upsv6_pub.git
cd upsv6_pub/
  • Step 2. Enable I2C function by using raspi-config command:
sudo raspi-config

Navigate to 3 Interface Options -> I2C -> YES -> OK -> Finished

  • Step 3. Execute Python script
python script/tools/python_demo/read_device_basic_demo.py 

How to send the UPS Gen 6's data to Home assistant

Modify the configuration on Home assistant

  • Please find the home assistant configuration.yaml file and adding following lines, In my case, I have use a docker container for my home assistant application.
sudo vim /opt/homeassistant/configuration.yaml

adding:

# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

mqtt:
  sensor:
    - name: "OUTPUT_VOLTAGE"
      state_topic: "ups_status/OUTPUT_VOLTAGE"
      unit_of_measurement: "mV"
      value_template: "{{ value | int }}"

    - name: "OUTPUT_CURRENT"
      state_topic: "ups_status/OUTPUT_CURRENT"
      unit_of_measurement: "mA"
      value_template: "{{ value | int }}"

    - name: "INPUT_VOLTAGE"
      state_topic: "ups_status/INPUT_VOLTAGE"
      unit_of_measurement: "mV"
      value_template: "{{ value | int }}"

    - name: "BATTERY_VOLTAGE"
      state_topic: "ups_status/BATTERY_VOLTAGE"
      unit_of_measurement: "mV"
      value_template: "{{ value | int }}"

    - name: "MCU_VOLTAGE"
      state_topic: "ups_status/MCU_VOLTAGE"
      unit_of_measurement: "mV"
      value_template: "{{ value | int }}"

    - name: "INPUT_CURRENT"
      state_topic: "ups_status/INPUT_CURRENT"
      unit_of_measurement: "mA"
      value_template: "{{ value | int }}"

    - name: "BATTERY_CURRENT"
      state_topic: "ups_status/BATTERY_CURRENT"
      unit_of_measurement: "mA"
      value_template: "{{ value | int }}"

    - name: "TEMPERATURE"
      state_topic: "ups_status/TEMPERATURE"
      unit_of_measurement: "C"
      value_template: "{{ value | int }}"
  • Restart Home assistant docker
sudo docker restart $(sudo docker ps -qa)

Create a python script on Raspberry Pi

  • filename: send_data_to_mqtt.py, please install paho library first.
pip install paho-mqtt --break-system-packages 

and then create a python script file and name it 'send_data_to_mqtt.py' with following:

from smbus2 import SMBus
import time
import paho.mqtt.client as mqtt

# MQTT server configure
MQTT_BROKER = "192.168.3.218"
MQTT_PORT = 1883
MQTT_USERNAME = "jacky"
MQTT_PASSWORD = "mypassword"
MQTT_TOPIC = "ups_status"

# Device address
DEVICE_ADDRESS = 0x17

# init SMBus
bus = SMBus(1)  # 1 means I2C no.1 bus

# init mqtt client
mqtt_client = mqtt.Client()
mqtt_client.username_pw_set(MQTT_USERNAME, password=MQTT_PASSWORD)

# connect to mqtt server
def connect_mqtt():
    try:
        mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
        print(f"{GREEN}Connected to MQTT Broker at {MQTT_BROKER}:{MQTT_PORT}{END_COLOR}")
    except Exception as e:
        print(f"{RED}Failed to connect to MQTT Broker: {e}{END_COLOR}")
        exit(1)


# Define registers address
# WHO_AM_I_REG = 0x00 // will always be 0xA6
VERSION_REG = 0x01
UID0_REG = 0x02
UID1_REG = 0x06
UID2_REG = 0x0A
OUTPUT_VOLTAGE_REG = 0x0E
INPUT_VOLTAGE_REG = 0x10
BATTERY_VOLTAGE_REG = 0x12
MCU_VOLTAGE_REG = 0x14
OUTPUT_CURRENT_REG = 0x16
INPUT_CURRENT_REG = 0x18
BATTERY_CURRENT_REG = 0x1A
TEMPERATURE_REG = 0x1C

# Define ANSI color
RED = '\033[95m'
GREEN = '\033[92m'
END_COLOR = '\033[0m'


# read 8bit register
def read_byte_register(register_address):
    value = bus.read_byte_data(DEVICE_ADDRESS, register_address)
    if value > 127:
        value -= 256
    return value

# read 16bit register
def read_word_register(register_address):
    value = bus.read_word_data(DEVICE_ADDRESS, register_address)
    if value > 32767:
        value -= 65536
    return value

# read 32bit register
def read_dword_register(register_address):
    low = bus.read_word_data(DEVICE_ADDRESS, register_address)
    high = bus.read_word_data(DEVICE_ADDRESS, register_address + 2)
    return (high << 16) | low

# read all registers data
def read_all_registers():
    registers = {
        "WHO_AM_I": "0xA6",
        "VERSION": read_byte_register(VERSION_REG),
        "UID0": read_dword_register(UID0_REG),
        "UID1": read_dword_register(UID1_REG),
        "UID2": read_dword_register(UID2_REG),
        "OUTPUT_VOLTAGE": read_word_register(OUTPUT_VOLTAGE_REG),
        "INPUT_VOLTAGE": read_word_register(INPUT_VOLTAGE_REG),
        "BATTERY_VOLTAGE": read_word_register(BATTERY_VOLTAGE_REG),
        "MCU_VOLTAGE": read_word_register(MCU_VOLTAGE_REG),
        "OUTPUT_CURRENT": read_word_register(OUTPUT_CURRENT_REG),
        "INPUT_CURRENT": read_word_register(INPUT_CURRENT_REG),
        "BATTERY_CURRENT": read_word_register(BATTERY_CURRENT_REG),
        "TEMPERATURE": read_byte_register(TEMPERATURE_REG)
    }
    return registers

# send data to MQTT server
def send_data_to_mqtt(data):
    try:
        for key, value in data.items():
            if key in ["OUTPUT_VOLTAGE", "INPUT_VOLTAGE", "BATTERY_VOLTAGE", "MCU_VOLTAGE"]:
                mqtt_client.publish(f"{MQTT_TOPIC}/{key}", value)
            elif key in ["OUTPUT_CURRENT", "INPUT_CURRENT", "BATTERY_CURRENT"]:
                mqtt_client.publish(f"{MQTT_TOPIC}/{key}", value)
            elif key == "TEMPERATURE":
                mqtt_client.publish(f"{MQTT_TOPIC}/{key}", value)
        print(f"{GREEN}Data sent to MQTT topic {MQTT_TOPIC}:{data}{END_COLOR}")
    except Exception as e:
        print(f"{RED}Failed to send data to MQTT broker:{e}{END_COLOR}")


# main loop
def main():
    connect_mqtt()
    mqtt_client.loop_start()  # start mqtt loop
    try:
        while True:
            print(f"{RED}===52Pi UPS v6 Raw data output ==={END_COLOR}")
            registers = read_all_registers()
            send_data_to_mqtt(registers)
            time.sleep(2)
    except KeyboardInterrupt:
        bus.close()
        mqtt_client.loop_stop()
        mqtt_client.disconnect()
        print("Quit Demo")


if __name__ == "__main__":
    main()

and then execute the python script:

python send_data_to_mqtt.py 
Send data to mqtt.jpg


  • Adding Card in Home assistant
Adding card to HA.jpg


  • Finally result
Result of ha.jpg


How to monitor battery and safe shutdown

Here is the modified code, rewritten as a systemd service to monitor only the battery voltage and current. When the battery voltage drops below 3700mV, it triggers a shutdown operation. The monitoring interval is set to check every 2 minutes.

  • Python Code (demo)
#!/usr/bin/env python3
"""
This script monitors the battery voltage and current of a 52Pi UPS V6 device.
If the battery voltage drops below 3700mV, it triggers a shutdown process.
"""

from smbus2 import SMBus
import time
import subprocess
import logging

#define log file path
LOG_FILE = "/var/log/battery_monitor.log"

# configure logging settings
logging.basicConfig(filename=LOG_FILE, level=logging.INFO, format='%(asctime)s - %(message)s')

# Device address
DEVICE_ADDRESS = 0x17

# Define registers address
BATTERY_VOLTAGE_REG = 0x12
BATTERY_CURRENT_REG = 0x1A

# Threshold for battery voltage
BATTERY_VOLTAGE_THRESHOLD = 7400  # in mV

# Shutdown command
SHUTDOWN_COMMAND = "sudo sync ; sudo init 0"

# Initialize SMBus
bus = SMBus(1)  # 1 means I2C no.1 bus

# Read 16-bit register
def read_word_register(register_address):
    """
    Read a 16-bit register from the device.
    """
    value = bus.read_word_data(DEVICE_ADDRESS, register_address)
    if value > 32767:
        value -= 65536
    return value

# Read battery voltage and current
def read_battery_status():
    """
    Read the battery voltage and current from the device.
    """
    battery_voltage = read_word_register(BATTERY_VOLTAGE_REG)
    battery_current = read_word_register(BATTERY_CURRENT_REG)
    return battery_voltage, battery_current

# Check battery status and trigger shutdown if necessary
def check_battery_status():
    """
    Check the battery status and trigger shutdown if the voltage is below the threshold.
    """
    battery_voltage, battery_current = read_battery_status()
    logging.info(f"Battery Voltage: {battery_voltage}mV, Current: {battery_current}mA.")

    if battery_voltage < BATTERY_VOLTAGE_THRESHOLD:
        logging.info(f"Battery voltage is below {BATTERY_VOLTAGE_THRESHOLD}mV. Initiating shutdown.")
        logging.info(f"Battery voltage is below {BATTERY_VOLTAGE_THRESHOLD}mV. Initiating shutdown.")
        subprocess.run(SHUTDOWN_COMMAND, shell=True)

# Main loop
def main():
    """
    Main loop to periodically check the battery status.
    """
    try:
        while True:
            check_battery_status()
            time.sleep(120)  # Check every 2 minutes
    except KeyboardInterrupt:
        print("Exiting battery monitor script.")
    finally:
        bus.close()

if __name__ == "__main__":
    main()

Please save it and name it as 'battery_monitory.py' and put it to your own location. in this case, the file location is `/home/pi/upsv6_pub/script/tools/python_demo/battery_monitor.py`.

Code Explanation

  • Optimization:
  - Removed unnecessary `read_byte_register` and `read_dword_register` functions, as only the battery voltage and current are monitored, both of which are 16-bit registers.
  - Removed the redundant `read_all_registers` function and directly read the battery voltage and current in `check_battery_status`.
  - Removed excess print statements, retaining only those for battery voltage and current.
  • Monitoring Logic:
  - Checks the battery voltage and current every 2 minutes.
  - If the battery voltage falls below 3700mV, it calls `subprocess.run` to execute the shutdown command.
  • Comments:
  - Added detailed English comments to explain the purpose of each function and the main logic.

Creating a systemd Service

To run this script as a systemd service, you need to create a systemd service file.

  • Create the Service File:
  Create a service file in the `/etc/systemd/system/` directory, for example, `battery_monitor.service`.
   sudo nano /etc/systemd/system/battery_monitor.service
  • Edit the Service File:
  Add the following content to the file:
   [Unit]
   Description=Battery Monitor Service
   After=network.target

   [Service]
   ExecStart=/usr/bin/python3 /path/to/your/script/battery_monitor.py   # Do remember replace this path to your own battery_monitor.py file's location. 
   Restart=always
   User=pi

   [Install]
   WantedBy=multi-user.target
  - `ExecStart` specifies the path to the script.
  - `Restart=always` ensures the service restarts automatically if it fails.
  - `User=pi` specifies that the script runs as the `pi` user.

Create log file and grant permissions

sudo touch /var/log/battery_monitor.log
sudo chown pi:pi /var/log/battery_monitor.log
sudo chmod 664 /var/log/battery_monitor.log

Start the Service

  After saving the file, run the following commands to start the service and enable it to start on boot:
   sudo systemctl daemon-reload
   sudo systemctl enable battery_monitor.service
   sudo systemctl start battery_monitor.service

Check Service Status

  Use the following command to check the service status:
   sudo systemctl status battery_monitor.service

With this setup, the script will run as a systemd service, checking the battery voltage and current every 2 minutes and triggering a shutdown operation when the voltage falls below 7400mV.

More information