Start properly documenting the protocol
This commit is contained in:
parent
c7e97aca84
commit
c367d642d8
5 changed files with 137 additions and 14 deletions
|
@ -11,8 +11,8 @@ charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[*.{py,yaml,yml}]
|
[*.{py,yaml,yml,md}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
[*.{yaml,yml}]
|
[*.{yaml,yml,md}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
|
@ -26,6 +26,8 @@ repos:
|
||||||
rev: 2.3.54
|
rev: 2.3.54
|
||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
|
args:
|
||||||
|
- "--exclude=Protocol.md"
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 4.0.1
|
rev: 4.0.1
|
||||||
|
@ -38,7 +40,7 @@ repos:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 21.9b0
|
rev: 21.10b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
|
||||||
|
|
85
Protocol.md
Normal file
85
Protocol.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# Protocol
|
||||||
|
|
||||||
|
The protocol checksum is litle-endian CRC16-MODBUS, but the data itself seems to be big-endian (network order).
|
||||||
|
It is structured around 16bit words (read 4 returns 8 bytes).
|
||||||
|
|
||||||
|
I have seen the "Transfer ID?" field containing 0xFF and 0x01, I am not yet sure of the putpose of this field. Most transactions it is set to 0xFF.
|
||||||
|
|
||||||
|
## Reading
|
||||||
|
|
||||||
|
### Read request
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────────┐
|
||||||
|
╔═╡ Transfer ID? │
|
||||||
|
║ └──────────────┘
|
||||||
|
║ ┌────────────────────┐
|
||||||
|
║ ╔═╡ Operation (3=read) │
|
||||||
|
║ ║ └────────────────────┘
|
||||||
|
║ ║ ┌───────────────┐
|
||||||
|
║ ║ ╔═╡ Start address │
|
||||||
|
║ ║ ║ └───────────────┘
|
||||||
|
║ ║ ║ ┌────────────────────────────────┐
|
||||||
|
║ ║ ║ ╔═╡ Number of 2-byte words to read │
|
||||||
|
║ ║ ║ ║ └────────────────────────────────┘
|
||||||
|
║ ║ ║ ║ ┌─────────────────┐
|
||||||
|
║ ║ ║ ║ ╔═╡ Transaction CRC │
|
||||||
|
║ ║ ║ ║ ║ └─────────────────┘
|
||||||
|
┌╨─┬╨─┬──╨──┬──╨──┬──╨──┐
|
||||||
|
│FF│03│00 0C│00 08│91 d1│
|
||||||
|
└──┴──┴─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read response
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────────┐
|
||||||
|
╔═╡ Transfer ID? │
|
||||||
|
║ └──────────────┘
|
||||||
|
║ ┌────────────────────┐
|
||||||
|
║ ╔═╡ Operation (3=read) │
|
||||||
|
║ ║ └────────────────────┘
|
||||||
|
║ ║ ┌──────────────────────────┐
|
||||||
|
║ ║ ╔═╡ Number of bytes returned │
|
||||||
|
║ ║ ║ └──────────────────────────┘
|
||||||
|
║ ║ ║ ┌──────┐ ┌─────────────────┐
|
||||||
|
║ ║ ║ ╔═╡ Data │ │ Transaction CRC ╞═╗
|
||||||
|
║ ║ ║ ║ └──────┘ └─────────────────┘ ║
|
||||||
|
┌╨─┬╨─┬╨─┬╨──────────────────────────────────────────────┬──╨──┐
|
||||||
|
│FF│03│10│20 20 20 20 4D 4C 32 34 32 30 20 20 20 20 20 20│FD 17│
|
||||||
|
└──┴──┴──┴───────────────────────────────────────────────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
This particular memory section contains the device SKU: ML2420
|
||||||
|
|
||||||
|
## Writing
|
||||||
|
|
||||||
|
### Write request
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────────┐
|
||||||
|
╔═╡ Transfer ID? │
|
||||||
|
║ └──────────────┘
|
||||||
|
║ ┌────────────────────┐
|
||||||
|
║ ╔═╡ Operation (6=read) │
|
||||||
|
║ ║ └────────────────────┘
|
||||||
|
║ ║ ┌─────────┐
|
||||||
|
║ ║ ╔═╡ Address │
|
||||||
|
║ ║ ║ └─────────┘
|
||||||
|
║ ║ ║ ┌──────┐
|
||||||
|
║ ║ ║ ╔═╡ Data │
|
||||||
|
║ ║ ║ ║ └──────┘
|
||||||
|
║ ║ ║ ║ ┌─────────────────┐
|
||||||
|
║ ║ ║ ║ ╔═╡ Transaction CRC │
|
||||||
|
║ ║ ║ ║ ║ └─────────────────┘
|
||||||
|
┌╨─┬╨─┬──╨──┬──╨──┬──╨──┐
|
||||||
|
│FF│06│01 0A│00 01│7C 2A│
|
||||||
|
└──┴──┴─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
The data at 0x010A is a boolean controlling the load output switch.
|
||||||
|
|
||||||
|
### Write response
|
||||||
|
|
||||||
|
Seems to return exactly the same as the data written,
|
||||||
|
presumably to allow verifying that the data did indeed get written.
|
15
Readme.md
Normal file
15
Readme.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# SolarMPPT
|
||||||
|
|
||||||
|
<!-- TODO: Come up with a better name -->
|
||||||
|
|
||||||
|
Python library for interracting with the rather generic MPPT solar charge controller I got from the hardware store.
|
||||||
|
|
||||||
|
- [Biltema 25-5077](https://www.biltema.no/bil---mc/elektrisk-anlegg/solcellspaneler/mppt-regulator-20-a-2000045547)
|
||||||
|
|
||||||
|
The Android app suggested for the bluetooth interface is
|
||||||
|
[SolarApp](https://play.google.com/store/apps/details?id=com.shuori.gfv2.guangfu) by srne
|
||||||
|
(I'm not currently able to find the bluetooth bridge on Biltema's website?
|
||||||
|
It's got BT-1 printed on the front, and is basically just a RS-232 to BTLE UART GATT)
|
||||||
|
|
||||||
|
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
|
||||||
|
![example workflow](https://github.com/oddstr13/SolarMPPT/actions/workflows/pre-commit/badge.svg)
|
43
solar_ble.py
43
solar_ble.py
|
@ -15,24 +15,33 @@ INTERVAL = 15
|
||||||
write_device = "0000ffd1-0000-1000-8000-00805f9b34fb"
|
write_device = "0000ffd1-0000-1000-8000-00805f9b34fb"
|
||||||
# read_device = "0000fff1-0000-1000-8000-00805f9b34fb"
|
# read_device = "0000fff1-0000-1000-8000-00805f9b34fb"
|
||||||
|
|
||||||
|
# get(255, 12, 2)
|
||||||
|
# "ff 03 00 0c 00 02"
|
||||||
CMD_GET_1 = b"\xff\x03\x00\x0c\x00\x02"
|
CMD_GET_1 = b"\xff\x03\x00\x0c\x00\x02"
|
||||||
# > ff 03 04 20 20 20 20
|
# > ff 03 04 20 20 20 20
|
||||||
|
|
||||||
|
# get(255, 12, 8)
|
||||||
|
# ff 03 00 0c 00 08
|
||||||
CMD_GET_MODEL = b"\xff\x03\x00\x0c\x00\x08"
|
CMD_GET_MODEL = b"\xff\x03\x00\x0c\x00\x08"
|
||||||
# > ff 03 10 20 20 20 20 4d 4c 32 34 32 30 20 20 20 20 20 20
|
# > ff 03 10 20 20 20 20 4d 4c 32 34 32 30 20 20 20 20 20 20
|
||||||
# Device SKU: ML2420
|
# Device SKU: ML2420
|
||||||
|
|
||||||
|
# get(255, 20, 4)
|
||||||
|
# ff 03 00 14 00 04
|
||||||
CMD_GET_VERSION = b"\xff\x03\x00\x14\x00\x04"
|
CMD_GET_VERSION = b"\xff\x03\x00\x14\x00\x04"
|
||||||
# > ff 03 08 00 04 02 00 02 00 00 03
|
# > ff 03 08 00 04 02 00 02 00 00 03
|
||||||
# CC ?? 11 22 33 ?? 44 55 66
|
# CC ?? 11 22 33 ?? 44 55 66
|
||||||
# Version: 4.2.0
|
# Version: 4.2.0
|
||||||
|
|
||||||
|
# get(255, 24, 3)
|
||||||
|
# ff 03 00 18 00 03
|
||||||
CMD_GET_SERIAL = b"\xff\x03\x00\x18\x00\x03"
|
CMD_GET_SERIAL = b"\xff\x03\x00\x18\x00\x03"
|
||||||
# > ff 03 06 3c 13 02 67 00 01
|
# > ff 03 06 3c 13 02 67 00 01
|
||||||
# CC 11 22 33 33 ?? ??
|
# CC 11 22 33 33 ?? ??
|
||||||
# SN: 60-19-0615
|
# SN: 60-19-0615
|
||||||
|
|
||||||
|
# get(255, 256, 7)
|
||||||
|
# ff 03 01 00 00 07
|
||||||
CMD_GET_BATTERY_STATE = b"\xff\x03\x01\x00\x00\x07"
|
CMD_GET_BATTERY_STATE = b"\xff\x03\x01\x00\x00\x07"
|
||||||
# > ff 03 0e 00 48 00 7e 00 1d 0e 0d 00 7e 00 1c 00 03
|
# > ff 03 0e 00 48 00 7e 00 1d 0e 0d 00 7e 00 1c 00 03
|
||||||
# CC 11 11 22 22 33 33 44 55 66 66 77 77 88 88
|
# CC 11 11 22 22 33 33 44 55 66 66 77 77 88 88
|
||||||
|
@ -45,6 +54,8 @@ CMD_GET_BATTERY_STATE = b"\xff\x03\x01\x00\x00\x07"
|
||||||
# 7: Load current: 0.28 A
|
# 7: Load current: 0.28 A
|
||||||
# 8: Load power: 3 W
|
# 8: Load power: 3 W
|
||||||
|
|
||||||
|
# get(255, 263, 4)
|
||||||
|
# ff 03 01 07 00 04
|
||||||
CMD_GET_PANEL_STATUS = b"\xff\x03\x01\x07\x00\x04"
|
CMD_GET_PANEL_STATUS = b"\xff\x03\x01\x07\x00\x04"
|
||||||
# > ff 03 08 00 c8 00 14 00 04 00 01
|
# > ff 03 08 00 c8 00 14 00 04 00 01
|
||||||
# CC 11 11 22 22 33 33 ?? ??
|
# CC 11 11 22 22 33 33 ?? ??
|
||||||
|
@ -53,6 +64,21 @@ CMD_GET_PANEL_STATUS = b"\xff\x03\x01\x07\x00\x04"
|
||||||
# 3: Panel power: 4 W
|
# 3: Panel power: 4 W
|
||||||
# Charging status?
|
# Charging status?
|
||||||
|
|
||||||
|
# set(255, 266, 1 or 0)
|
||||||
|
# ff 06 01 0a 00 01
|
||||||
|
CMD_ENABLE_LOAD = b"\xff\x06\x01\x0a\x00\x01"
|
||||||
|
CMD_DISABLE_LOAD = b"\xff\x06\x01\x0a\x00\x00"
|
||||||
|
REG_LOAD_ENABLE = 0x010A
|
||||||
|
|
||||||
|
# get(255, 267, 21)
|
||||||
|
# ff 03 01 0b 00 15
|
||||||
|
CMD_GET_LOAD_PARAMETERS = b"\xff\x03\x01\x0b\x00\x15"
|
||||||
|
# > ff 03 2a 00 7c 00 7f 00 51 00 20 00 0a 00 03 00 00 00 00 00
|
||||||
|
# > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
# > 00 00 00 00 00
|
||||||
|
|
||||||
|
# get(255, 288, 3)
|
||||||
|
# ff 03 01 20 00 03
|
||||||
CMD_GET_2 = b"\xff\x03\x01\x20\x00\x03"
|
CMD_GET_2 = b"\xff\x03\x01\x20\x00\x03"
|
||||||
# > ff 03 06 80 02 00 00 00 00
|
# > ff 03 06 80 02 00 00 00 00
|
||||||
# CC 11 22 33 33 33 33
|
# CC 11 22 33 33 33 33
|
||||||
|
@ -60,6 +86,8 @@ CMD_GET_2 = b"\xff\x03\x01\x20\x00\x03"
|
||||||
# 2: ?: 2
|
# 2: ?: 2
|
||||||
# 3: ?: 0
|
# 3: ?: 0
|
||||||
|
|
||||||
|
# get(255, 57345, 33)
|
||||||
|
# ff 03 e0 01 00 21
|
||||||
CMD_GET_BATTERY_PARAMETERS = b"\xff\x03\xe0\x01\x00\x21"
|
CMD_GET_BATTERY_PARAMETERS = b"\xff\x03\xe0\x01\x00\x21"
|
||||||
# > ff 03 42 07 d0 00 c8 ff 0c 00 02 00 a0 00 9b 00 92 00 90 00
|
# > ff 03 42 07 d0 00 c8 ff 0c 00 02 00 a0 00 9b 00 92 00 90 00
|
||||||
# > 8a 00 84 00 7e 00 78 00 6f 00 6a 64 32 00 05 00 78 00 78 00
|
# > 8a 00 84 00 7e 00 78 00 6f 00 6a 64 32 00 05 00 78 00 78 00
|
||||||
|
@ -67,13 +95,8 @@ CMD_GET_BATTERY_PARAMETERS = b"\xff\x03\xe0\x01\x00\x21"
|
||||||
# > 0f 00 05 00 05 00 04 01 00
|
# > 0f 00 05 00 05 00 04 01 00
|
||||||
# 33 * uint16
|
# 33 * uint16
|
||||||
|
|
||||||
# (0xff, 267, 21)
|
# get(1, 61440, 10)
|
||||||
CMD_GET_LOAD_PARAMETERS = b"\xff\x03\x01\x0b\x00\x15"
|
# 01 03 f0 00 00 0a
|
||||||
# > ff 03 2a 00 7c 00 7f 00 51 00 20 00 0a 00 03 00 00 00 00 00
|
|
||||||
# > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
||||||
# > 00 00 00 00 00
|
|
||||||
|
|
||||||
# 01 03 f000 000a
|
|
||||||
CMD_GET_HISTORICAL_TODAY = b"\x01\x03\xf0\x00\x00\x0a"
|
CMD_GET_HISTORICAL_TODAY = b"\x01\x03\xf0\x00\x00\x0a"
|
||||||
CMD_GET_HISTORICAL_YESTERDAY = b"\x01\x03\xf0\x01\x00\x0a"
|
CMD_GET_HISTORICAL_YESTERDAY = b"\x01\x03\xf0\x01\x00\x0a"
|
||||||
CMD_GET_HISTORICAL_D2 = b"\x01\x03\xf0\x02\x00\x0a"
|
CMD_GET_HISTORICAL_D2 = b"\x01\x03\xf0\x02\x00\x0a"
|
||||||
|
@ -103,9 +126,7 @@ CMD_GET_HISTORICAL_D3 = b"\x01\x03\xf0\x03\x00\x0a"
|
||||||
# production_power = 0 Wh
|
# production_power = 0 Wh
|
||||||
# consumption_power = 0 Wh
|
# consumption_power = 0 Wh
|
||||||
|
|
||||||
CMD_ENABLE_LOAD = b"\xff\x06\x01\x0a\x00\x01"
|
# ff 78 00 00 00 01
|
||||||
CMD_DISABLE_LOAD = b"\xff\x06\x01\x0a\x00\x00"
|
|
||||||
|
|
||||||
CMD_ = b"\xff\x78\x00\x00\x00\x01"
|
CMD_ = b"\xff\x78\x00\x00\x00\x01"
|
||||||
|
|
||||||
# CMD_GET_BATTERY_STATE = b'\xff\x03\x01\x00\x00\x07'
|
# CMD_GET_BATTERY_STATE = b'\xff\x03\x01\x00\x00\x07'
|
||||||
|
|
Loading…
Reference in a new issue