voltgo

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2026 License: MIT Imports: 7 Imported by: 0

README

Voltgo

A Go library for communicating with Voltgo (and compatible) LiFePO4 batteries via Bluetooth Low Energy (BLE).

These batteries are sold under various brand names including Enerwatt, TCED Worldwide, and others, but all use the same BLE protocol compatible with the Voltgo mobile app.

This library provides a simple interface to connect to and monitor LiFePO4 batteries that use a BMS with Bluetooth support, compatible with the Voltgo mobile app.

Features

  • BLE communication with Voltgo LiFePO4 battery BMS
  • Read battery status (voltage, current, SOC, SOH, temperatures)
  • Read individual cell voltages
  • Read device info (model, capacity, manufacture date)
  • Modbus RTU over BLE GATT protocol, verified against real hardware
  • Cross-platform support (Linux, macOS, Windows)
  • Built on TinyGo Bluetooth library
  • Compatible with Enerwatt, TCED Worldwide, and other branded batteries using the Voltgo protocol

Installation

go get github.com/lumberbarons/voltgo

Requirements

  • Go 1.24 or later
  • Bluetooth Low Energy adapter
  • Platform-specific Bluetooth support:
    • Linux: BlueZ
    • macOS: CoreBluetooth (built-in)
    • Windows: WinRT Bluetooth API

Project Structure

voltgo/
├── battery/           # Battery data structures and types
│   └── types.go
├── ble/              # BLE connection handling
│   ├── connection.go
│   └── uuids.go
├── protocol/         # Modbus RTU framing and register parsing
│   ├── modbus.go
│   └── parser.go
├── examples/         # Example applications
│   ├── basic/
│   ├── monitor/
│   └── scan/
├── cmd/              # CLI tools
│   └── voltgo-cli/
├── client.go         # Main client interface
├── doc.go
├── Makefile
├── CONTRIBUTING.md   # Contributor guide
├── PROTOCOL.md       # Reverse-engineered protocol documentation
├── go.mod
└── README.md

Quick Start

Scanning for Batteries
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/lumberbarons/voltgo"
)

func main() {
    ctx := context.Background()

    client, err := voltgo.NewClient()
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Scan for 10 seconds (returns raw BLE scan results)
    results, err := client.ScanRaw(ctx, 10*time.Second)
    if err != nil {
        log.Fatal(err)
    }

    for i, result := range results {
        fmt.Printf("%d. %s (%s) - RSSI: %d dBm\n",
            i+1, result.LocalName(), result.Address.String(), result.RSSI)
    }
}

You should see output like:

1. ZT-25.6V100Ah-1238 (a4:c1:37:43:a4:42) - RSSI: -62 dBm

If nothing shows up:

  • On Linux, make sure your user has permission to use the Bluetooth adapter (typically membership in the bluetooth group) and that bluetoothd is running.
  • A battery that is already connected to the Voltgo phone app stops advertising and won't appear in scans — disconnect the app first.
Reading Battery Status
// Connect to a battery device by address
battery, err := client.Connect(ctx, results[0].Address)
if err != nil {
    log.Fatal(err)
}
defer battery.Disconnect()

// Get current status
status, err := battery.GetStatus(ctx)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Voltage: %.2fV\n", status.Voltage)
fmt.Printf("Current: %.2fA\n", status.Current)
fmt.Printf("SOC: %d%%\n", status.SOC)
fmt.Printf("Temperature: %.1f°C\n", status.Temperature)

// Get individual cell voltages
cells, err := battery.GetCellVoltages(ctx)
if err != nil {
    log.Fatal(err)
}

for _, cell := range cells {
    fmt.Printf("Cell %d: %.3fV\n", cell.Index+1, cell.Voltage)
}

Protocol Details

For protocol details, see PROTOCOL.md.

Examples

See the examples/ directory for complete working examples:

  • examples/scan/ - Simple device scanner
  • examples/basic/ - Basic battery communication example
  • examples/monitor/ - Continuous battery monitoring

API Reference

Client
// Create a new client
client, err := voltgo.NewClient()

// Scan for devices (returns display-oriented battery.DeviceInfo)
devices, err := client.Scan(ctx, duration)

// Scan returning raw BLE results — use this when you intend to connect
results, err := client.ScanRaw(ctx, duration)

// Connect to a device by BLE address
battery, err := client.Connect(ctx, results[0].Address)

// Connect to a device by scan result index
battery, err := client.ConnectByIndex(ctx, results, 0)

// Close the client
client.Close()
Battery
// Check connection status
isConnected := battery.IsConnected()

// Get battery status
status, err := battery.GetStatus(ctx)

// Get cell voltages
cells, err := battery.GetCellVoltages(ctx)

// Get battery info
info, err := battery.GetInfo(ctx)

// Get the parsed status register block, including raw register values
bmsInfo, err := battery.GetBMSInfo(ctx)

// Get the ASCII device-info register block (model, hw version, date)
devInfo, err := battery.GetDeviceInfo(ctx)

// Read raw Modbus holding registers
regs, err := battery.ReadRegisters(ctx, startReg, count)

// Disconnect
battery.Disconnect()

Development Status

The protocol (Modbus RTU over BLE GATT) has been reverse-engineered from HCI traces and live probing, and is verified working against real ZT-25.6V100Ah batteries on Linux/BlueZ.

Known gaps (see PROTOCOL.md):

  • Current scaling/sign is assumed (int16, 0.1A) but has only been observed at 0A idle
  • Status/protection flag registers are unmapped (all zero on a healthy battery)
  • Write commands (charge/discharge switches, heating) are not yet implemented

Contributing

Contributions welcome! If you have a compatible battery, please test and report results.

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

License

MIT

Acknowledgments

  • Protocol analysis based on the Voltgo Android application
  • Built with TinyGo Bluetooth library

Documentation

Overview

Package voltgo provides a Go library for communicating with Voltgo and compatible LiFePO4 batteries via Bluetooth Low Energy (BLE).

These batteries are sold under various brand names including Enerwatt, TCED Worldwide, and others, but use the same BLE protocol compatible with the Voltgo mobile app.

This library implements the BLE protocol used by the Voltgo mobile app to monitor and control LiFePO4 battery management systems (BMS).

Basic usage:

client, err := voltgo.NewClient()
if err != nil {
	log.Fatal(err)
}
defer client.Close()

// Scan for batteries (raw results carry the address needed to connect)
results, err := client.ScanRaw(ctx, 10*time.Second)
if err != nil {
	log.Fatal(err)
}

// Connect to a device
battery, err := client.Connect(ctx, results[0].Address)
if err != nil {
	log.Fatal(err)
}
defer battery.Disconnect()

// Read battery status
status, err := battery.GetStatus(ctx)
if err != nil {
	log.Fatal(err)
}

The library is organized into several packages:

  • battery: Data structures for battery status, cell information, and device info
  • ble: Low-level BLE connection handling and characteristic I/O
  • protocol: Modbus RTU framing and register parsing

Protocol Details:

The BLE protocol uses the following UUIDs:

  • Service: 00001006-0000-1000-8000-00805f9b34fb
  • Write: 00001008-0000-1000-8000-00805f9b34fb
  • Notify: 00001007-0000-1000-8000-00805f9b34fb

The batteries speak Modbus RTU framed over GATT: a standard Modbus read-holding-registers request (slave address 0x01, function 0x03, CRC-16/MODBUS) is written to the write characteristic, and the response frame arrives as a notification on the notify characteristic. Frames with an invalid CRC are silently ignored by the BMS. See PROTOCOL.md for the register map.

Index

Constants

View Source
const (
	DefaultTimeout      = 5 * time.Second
	DefaultScanDuration = 10 * time.Second
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Battery

type Battery struct {
	// contains filtered or unexported fields
}

Battery represents a connected battery

func (*Battery) Disconnect

func (b *Battery) Disconnect() error

Disconnect disconnects from the battery

func (*Battery) GetBMSInfo

func (b *Battery) GetBMSInfo(ctx context.Context) (*protocol.BMSInfo, error)

GetBMSInfo reads and parses the status register block. This is the low-level variant of GetStatus and includes the raw registers.

func (*Battery) GetCellVoltages

func (b *Battery) GetCellVoltages(ctx context.Context) ([]battery.Cell, error)

GetCellVoltages retrieves individual cell voltages

func (*Battery) GetDeviceInfo

func (b *Battery) GetDeviceInfo(ctx context.Context) (*protocol.DeviceInfo, error)

GetDeviceInfo reads the ASCII device-info register block.

func (*Battery) GetInfo

func (b *Battery) GetInfo(ctx context.Context) (*battery.Info, error)

GetInfo retrieves battery identity information: chemistry and nominal voltage derived from the cell count, plus the device's ASCII identity strings (model, hardware version, manufacture date).

func (*Battery) GetStatus

func (b *Battery) GetStatus(ctx context.Context) (*battery.Status, error)

GetStatus retrieves the current battery status

func (*Battery) IsConnected

func (b *Battery) IsConnected() bool

IsConnected returns whether the battery is connected

func (*Battery) ReadRegisters

func (b *Battery) ReadRegisters(ctx context.Context, startReg, count uint16) ([]uint16, error)

ReadRegisters reads count holding registers starting at startReg. This is the low-level primitive underlying all queries; the battery silently ignores malformed requests, which surfaces here as a timeout.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the main client for communicating with Voltgo batteries

func NewClient

func NewClient() (*Client, error)

NewClient creates a new Voltgo client

func (*Client) Close

func (c *Client) Close() error

Close closes the client and releases resources

func (*Client) Connect

func (c *Client) Connect(ctx context.Context, address bluetooth.Address) (*Battery, error)

Connect connects to a battery device by address

func (*Client) ConnectByIndex

func (c *Client) ConnectByIndex(ctx context.Context, results []bluetooth.ScanResult, index int) (*Battery, error)

ConnectByIndex connects to a battery device by scan result index

func (*Client) Scan

func (c *Client) Scan(ctx context.Context, duration time.Duration) ([]battery.DeviceInfo, error)

Scan scans for nearby batteries and returns device info

func (*Client) ScanRaw

func (c *Client) ScanRaw(ctx context.Context, duration time.Duration) ([]bluetooth.ScanResult, error)

ScanRaw scans for nearby batteries and returns raw scan results

Directories

Path Synopsis
cmd
voltgo-cli command
examples
basic command
monitor command
scan command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL