mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-21 12:56:11 +02:00
Merged local changes. JSON payloads now contain all known information.
This commit is contained in:
commit
79192bbede
5 changed files with 400 additions and 100 deletions
|
@ -1,4 +1,11 @@
|
||||||

|

|
||||||
|
|
||||||
# ahoy
|
# ahoy
|
||||||
Various tools, examples, and documentation for communicating with Hoymiles microinverters
|
Various tools, examples, and documentation for communicating with Hoymiles microinverters.
|
||||||
|
|
||||||
|
In particular:
|
||||||
|
|
||||||
|
* `doc\hoymiles-format-description.txt` is a detailed description of the communications format and the history of this project
|
||||||
|
* The `tools` folder contains various software tools for RaspberryPi and Arduino
|
||||||
|
|
||||||
|
Contributors are always welcome!
|
||||||
|
|
|
@ -1,42 +1,86 @@
|
||||||
Ziel dieses Projekts
|
About this Document
|
||||||
====================
|
===================
|
||||||
|
|
||||||
Anstelle der DTU wollen wir direkt von einem Arduino/RaspberryPi o.ä.
|
This description aims to document the data format that Hoymiles
|
||||||
die aktuellen Betriebsdaten der Wechselrichter auslesen.
|
micro inverters use to communicate their current operating state.
|
||||||
|
|
||||||
Ohne Umweg über die "Cloud".
|
The original Hoymiles setup requires connectivity to "the cloud",
|
||||||
|
see [this section below](#system-description).
|
||||||
|
|
||||||
|
With the information documented here, it is possible to interact with
|
||||||
|
a set of Hoymiles micro inverters in a purely "offline" way, i.e.
|
||||||
|
without requiring internet access or any connectivity to a "cloud".
|
||||||
|
|
||||||
|
The only required hardware is a Nordic "NRF24L01+" wireless module.
|
||||||
|
|
||||||
|
The `ahoy` project at [5] collects software for various platforms,
|
||||||
|
including Aduino and RaspberryPi.
|
||||||
|
|
||||||
|
> Note: Some of the sections in this document are (still) in German. Translations
|
||||||
|
> may be provided if and when necessary.
|
||||||
|
|
||||||
|
|
||||||
|
Origin, Contributors
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The information in this document was gathered in a large community
|
||||||
|
effort which started out with [this post][1]
|
||||||
|
on the German [mikrocontroller.net][2] forum.
|
||||||
|
|
||||||
|
As of April 2022, this effort is still ongoing. Not all details have
|
||||||
|
been documented yet, and not all secrets have been uncovered.
|
||||||
|
|
||||||
|
Multiple members of the community have already successfully retrieved (and
|
||||||
|
continue to successfully retrieve) data from their Hoymiles micro inverters.
|
||||||
|
|
||||||
|
Here's a list of some of the early contributors:
|
||||||
|
|
||||||
|
- sorbit: created the original mikrocontroller.net thread
|
||||||
|
- Martin (Gast): DTU and RF analysis
|
||||||
|
- Hubi: protocol analysis
|
||||||
|
- Marcel: initial analysis and much logging and interpretation
|
||||||
|
- Pascal A. (pasarn): various datagram fields, crc8
|
||||||
|
- Frank H. (fh_): discovered time_t
|
||||||
|
- Thomas B. (tbnobody): protocol analysis, logging
|
||||||
|
- Arnaldo G. (arnaldo_g): data capturing
|
||||||
|
- Oliver F (of22): protocol analysis, logging
|
||||||
|
- Martin G. (petersilie): protocol analysis, logging, RaspberryPi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Systemaufbau
|
System Description
|
||||||
============
|
==================
|
||||||
|
|
||||||
|
Ein Setup wie von Hoymiles vorgesehen, sieht wie folgt aus:
|
||||||
|
|
||||||
- Eine "DTU" kommuniziert mit vielen Wechselrichtern.
|
- Eine "DTU" kommuniziert mit vielen Wechselrichtern.
|
||||||
- Die Kommunikation geht immer von der DTU aus:
|
- Die Kommunikation geht immer von der DTU aus:
|
||||||
DTU stellt Anfrage und erwartet eine Antwort vom WR.
|
DTU stellt Anfrage und erwartet eine Antwort vom WR.
|
||||||
- Dafür muss die DTU die Adressen aller WR kennen.
|
- Dafür muss die DTU die Adressen (=Seriennummern) aller WR kennen.
|
||||||
|
- Diese werden der DTU im Rahmen eines Einrichtungsprozesses beigebracht.
|
||||||
|
|
||||||
|
```
|
||||||
Nordic
|
Nordic
|
||||||
"Shockburst"
|
"Enh. Shockburst"
|
||||||
2.4 GHz
|
2.4 GHz
|
||||||
\|/ <-----------------> \|/
|
\|/ <-----------------> \|/
|
||||||
| |
|
| |
|
||||||
+-------+ +-----------+
|
+-------+ +-----------+
|
||||||
| DTU | | MI-600 |
|
| DTU | | MI-600 |
|
||||||
+-------+ +-----------+-+
|
+-------+ +-----------+-+
|
||||||
| MI-600 |
|
|
||||||
+-----------+-+
|
|
||||||
| MI-1500 |
|
| MI-1500 |
|
||||||
|
+-----------+-+
|
||||||
|
| MI-... |
|
||||||
+-----------+
|
+-----------+
|
||||||
:
|
:
|
||||||
:
|
:
|
||||||
ABBILDUNG 1: Systemübersicht
|
ABBILDUNG 1: Systemübersicht
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
Nordic
|
Nordic
|
||||||
WLAN "Shockburst"
|
WLAN "Enh. Shockburst"
|
||||||
2.4 GHz
|
2.4 GHz
|
||||||
\|/ \|/
|
\|/ \|/
|
||||||
| |
|
| |
|
||||||
|
@ -50,11 +94,12 @@ Systemaufbau
|
||||||
(B) +----------+ (C)
|
(B) +----------+ (C)
|
||||||
|
|
||||||
ABBILDUNG 2: Innerer Aufbau "DTU"
|
ABBILDUNG 2: Innerer Aufbau "DTU"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
Nordic
|
Nordic
|
||||||
"Shockburst"
|
"Enh. Shockburst"
|
||||||
NRF24LE1E 2.4 GHz
|
NRF24LE1E 2.4 GHz
|
||||||
+------------------+ \|/
|
+------------------+ \|/
|
||||||
+----------+ | | | |
|
+----------+ | | | |
|
||||||
|
@ -63,16 +108,17 @@ Systemaufbau
|
||||||
+------+-----------+
|
+------+-----------+
|
||||||
|
|
||||||
ABBILDUNG 3: Detailansicht GD32F303 - NRF24LE1E
|
ABBILDUNG 3: Detailansicht GD32F303 - NRF24LE1E
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Adressierung
|
Adressierung
|
||||||
============
|
============
|
||||||
|
|
||||||
Die Seriennummern der DTU und der WR werden wie folgt in Adressen für die
|
Die Seriennummern der DTU und der WR werden wie folgt als Adressen für die
|
||||||
Kommunikation verwendet:
|
Kommunikation verwendet:
|
||||||
|
|
||||||
Interne Kommunikation: Die meisten Datenpakete enthalten Quell- und
|
**Interne Kommunikation**: Die meisten Datenpakete enthalten Quell- und
|
||||||
Zieladresse der jeweiligen Gesprächspartner. Hier werden 4-Byte-Adressen
|
Zieladresse der jeweiligen Gesprächspartner. Hier werden 4-Byte-Adressen
|
||||||
verwendet, die direkt aus den letzten 8 Stellen der Seriennummer des
|
verwendet, die direkt aus den letzten 8 Stellen der Seriennummer des
|
||||||
Wechselrichters bzw. der DTU gewonnen werden:
|
Wechselrichters bzw. der DTU gewonnen werden:
|
||||||
|
@ -83,20 +129,29 @@ Innerhalb der Pakete auf (C) wird daraus die 4-Byte-Adresse
|
||||||
0x72, 0x81, 0x88, 0x32 gebildet. Das ist die BCD-Darstellung
|
0x72, 0x81, 0x88, 0x32 gebildet. Das ist die BCD-Darstellung
|
||||||
der letzen 8 Dezimalziffern.
|
der letzen 8 Dezimalziffern.
|
||||||
|
|
||||||
NRF24-Kommunikation: Die zugehörige Shockburst Zieladresse ist
|
**NRF24 addressing scheme**: Over the air, the inverters communicate using
|
||||||
ähnlich, aber die Byte-Reihenfolge wird umgedreht, und es wird ein 0x01-Byte
|
the [Nordic "Enhanced Shockburst" Protocol][3] configured for
|
||||||
am Ende ergänzt (Shockburst ist auf 5-Byte-Adressen eingestellt).
|
5-byte addresses.
|
||||||
|
|
||||||
Um eine Nachricht an das Gerät mit o.g. Seriennummer zu senden
|
The inverter serial number is converted into a "Shockburst" address
|
||||||
lautet die Shockburst-Zieladresse also (0x32, 0x88, 0x81, 0x72, 0x01).
|
as follows:
|
||||||
|
|
||||||
|
- encode the final 8 digits of the serial number in BCD format:
|
||||||
|
`0x72, 0x81, 0x88, 0x32`
|
||||||
|
- reverse the order of the bytes:
|
||||||
|
`0x32, 0x88, 0x81, 0x72`
|
||||||
|
- append a byte containing 0x01:
|
||||||
|
`0x32, 0x88, 0x81, 0x72, 0x01`
|
||||||
|
|
||||||
Additional example, this time for inverter with serial number 99973104619:
|
In this example, the resulting "Shockburst" address is: 0x3288817201.
|
||||||
|
|
||||||
|
**Additional example**, this time for inverter with serial number 99973104619:
|
||||||
|
|
||||||
The datasheet specifies the over-the-air packet format: "Most Significant Byte
|
The datasheet specifies the over-the-air packet format: "Most Significant Byte
|
||||||
(MSB) to the left" (cf figure 11)
|
(MSB) to the left" (cf [datasheet figure 11][3])
|
||||||
|
|
||||||
Address := Byte_4, Byte_3, Byte_2, Byte_1, Byte_0 ("LSByte must be unique")
|
Address := Byte_4, Byte_3, Byte_2, Byte_1, Byte_0
|
||||||
|
("LSByte must be unique")
|
||||||
|
|
||||||
so 0x1946107301 results in
|
so 0x1946107301 results in
|
||||||
|
|
||||||
|
@ -105,28 +160,78 @@ so 0x1946107301 results in
|
||||||
Old-style NRF Libraries take uint64_t addresses. In this case, the correct
|
Old-style NRF Libraries take uint64_t addresses. In this case, the correct
|
||||||
address to pass to the library would be (uint64_t)0x1946107301ULL.
|
address to pass to the library would be (uint64_t)0x1946107301ULL.
|
||||||
|
|
||||||
https://nrf24.github.io actually wants uint8_t*, which maybe makes more sense.
|
The ["Optimized high speed nRF24L01+ driver"][4]
|
||||||
|
actually wants `uint8_t*`, which maybe makes more sense.
|
||||||
But apparently it still wants the bytes in order LSB to MSB (even though the chip will
|
But apparently it still wants the bytes in order LSB to MSB (even though the chip will
|
||||||
then put them out in MSB-to-LSB order.
|
then put them out in MSB-to-LSB order.
|
||||||
|
|
||||||
So in this case,
|
So in this case, the correct sequence of bytes to pass to the library
|
||||||
the correct sequence of bytes to pass to the library would be \x01\x73\x10\x46\x19.
|
would be "\x01\x73\x10\x46\x19".
|
||||||
|
|
||||||
|
Figure 4 below is an annotated example of an "Enhanced Shockburst" packet as
|
||||||
|
seen on the air.
|
||||||
|
|
||||||
|
```
|
||||||
|
+----------+--------------------+--------------------+---------------------+------------+
|
||||||
|
| preamble | dst 5-byte-address | PCF (9-bit) | payload (>=1 bytes) | 2-byte-CRC |
|
||||||
|
+----------+--------------------+--------------------+---------------------+------------+
|
||||||
|
| | | e.g. 0x0d8: | | |
|
||||||
|
| 0x55 | addr[4]...addr[0] | 0b011011 00 0 | | |
|
||||||
|
| or | MSB ... LSB | len=27 PID nACK | | |
|
||||||
|
| 0xAA | | | | |
|
||||||
|
| | | e.g. 0x0da | | |
|
||||||
|
| | | 0b011011 01 0 | | |
|
||||||
|
| | | len027 PID nACK | | |
|
||||||
|
+----------+--------------------+--------------------+---------------------+------------+
|
||||||
|
|
||||||
|
PCF: Packet control field
|
||||||
|
PID: Packet IDentification (to detect/avoid duplicates), cycles through 0...3
|
||||||
|
|
||||||
|
FIGURE 4: Enhanced Shockburst On-Air Data Format
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Nachrichten
|
Messages
|
||||||
===========
|
========
|
||||||
|
|
||||||
|
Initial protocol analysis focused on the data exchanged on link (C) in figure (3).
|
||||||
|
Not all the frames observed on this link will result in an actual RF transmission,
|
||||||
|
and some translation/mangling/processing happens inside the NRF24LE1E, in particular
|
||||||
|
|
||||||
|
- replacement of serial numbers
|
||||||
|
- recalculation of CRCs
|
||||||
|
|
||||||
|
These packets (which are all framed in 0x7e...0x7f bytes) are described in section
|
||||||
|
[Encapsulated Packets](#encapsulated-packets) below.
|
||||||
|
|
||||||
|
More recent efforts focus mainly on the actual "Enhanced Shockburst" packets
|
||||||
|
that are transmitted over the air. These packets are described in section
|
||||||
|
[Enhanced Shockburst Payloads](#Enhanced-Shockburst-Payloads), and the
|
||||||
|
information contained in this section is more up to date.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Encapsulated Packets
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
These are packets as observed on Link (C) in figure (3).
|
||||||
|
|
||||||
|
|
||||||
Nachricht: DTU an WR: "Init" (?)
|
Nachricht: DTU an WR: "Init" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
7E 07 00 00 00 00 00 00 00 00 00 07 7F
|
7E 07 00 00 00 00 00 00 00 00 00 07 7F
|
||||||
^^ ^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^
|
^^ ^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^
|
||||||
Bedeutung SOF MID WR ser# WR ser# ? CRC8 EOF
|
Bedeutung SOF MID WR ser# WR ser# ? CRC8 EOF
|
||||||
|
```
|
||||||
?
|
?
|
||||||
|
|
||||||
|
|
||||||
Nachricht: DTU an WR: "Init 2" (?)
|
Nachricht: DTU an WR: "Init 2" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
7E 07 72 81 88 32 72 81 88 32 00 07 7F
|
7E 07 72 81 88 32 72 81 88 32 00 07 7F
|
||||||
|
@ -134,10 +239,12 @@ Nachricht: DTU an WR: "Init 2" (?)
|
||||||
Bedeutung SOF MID DTU ser# DTU ser# ? CRC8 EOF
|
Bedeutung SOF MID DTU ser# DTU ser# ? CRC8 EOF
|
||||||
Einheit BCD (letzte 8) BCD (letzte 8) ? ?
|
Einheit BCD (letzte 8) BCD (letzte 8) ? ?
|
||||||
Beispiel 72818832 72818832 ?
|
Beispiel 72818832 72818832 ?
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x80: DTU an WR: "Zeit setzen" (?)
|
Nachricht 0x80: DTU an WR: "Zeit setzen" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|<-------------CRC16 'modbus' für CRC_M----------------->|
|
|<-------------CRC16 'modbus' für CRC_M----------------->|
|
||||||
7E 15 72 22 02 00 72 22 02 00 80 0B 00 62 09 04 9b 00 00 00 00 00 00 00 00 F2 68 F0 7F
|
7E 15 72 22 02 00 72 22 02 00 80 0B 00 62 09 04 9b 00 00 00 00 00 00 00 00 F2 68 F0 7F
|
||||||
|
@ -145,10 +252,12 @@ Nachricht 0x80: DTU an WR: "Zeit setzen" (?)
|
||||||
Bedeutung SOF MID WR ser# WR ser# CMD ? TIME (UTC) CRC_M CRC8 EOF
|
Bedeutung SOF MID WR ser# WR ser# CMD ? TIME (UTC) CRC_M CRC8 EOF
|
||||||
Einheit BCD (letzte 8) BCD (letzte 8) ? [s] HI LO
|
Einheit BCD (letzte 8) BCD (letzte 8) ? [s] HI LO
|
||||||
Beispiel 72220200 72220200 ? 2022-02-13
|
Beispiel 72220200 72220200 ? 2022-02-13
|
||||||
|
```
|
||||||
13:16:11
|
13:16:11
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x81: DTU an WR: "Anfrage DC-Daten" (?)
|
Nachricht 0x81: DTU an WR: "Anfrage DC-Daten" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
GD->NRF 7E 15 70 51 43 68 70 51 43 68 81 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
GD->NRF 7E 15 70 51 43 68 70 51 43 68 81 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
||||||
|
@ -160,9 +269,11 @@ GD->NRF 7E 15 70 51 43 68 70 51 43 68 81 xx 7F ..
|
||||||
on-air 15 70 51 43 68 70 53 54 53 81 BA
|
on-air 15 70 51 43 68 70 53 54 53 81 BA
|
||||||
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
||||||
WR ser # DTU ser #
|
WR ser # DTU ser #
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x82: DTU an WR: "Anfrage AC-Daten" (?)
|
Nachricht 0x82: DTU an WR: "Anfrage AC-Daten" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
GD->NRF 7E 15 70 51 43 68 70 51 43 68 82 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
GD->NRF 7E 15 70 51 43 68 70 51 43 68 82 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
||||||
|
@ -174,9 +285,11 @@ GD->NRF 7E 15 70 51 43 68 70 51 43 68 82 xx 7F ..
|
||||||
on-air 15 70 51 43 68 70 53 54 53 82 B9
|
on-air 15 70 51 43 68 70 53 54 53 82 B9
|
||||||
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
||||||
WR ser # DTU ser #
|
WR ser # DTU ser #
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x83: DTU an WR: "Anfrage DC-Daten" (?)
|
Nachricht 0x83: DTU an WR: "Anfrage DC-Daten" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
GD->NRF 7E 15 70 51 43 68 70 51 43 68 83 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
GD->NRF 7E 15 70 51 43 68 70 51 43 68 83 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
||||||
|
@ -188,9 +301,11 @@ GD->NRF 7E 15 70 51 43 68 70 51 43 68 83 xx 7F ..
|
||||||
on-air 15 70 51 43 68 70 53 54 53 83 B8
|
on-air 15 70 51 43 68 70 53 54 53 83 B8
|
||||||
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
||||||
WR ser # DTU ser #
|
WR ser # DTU ser #
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x85: DTU an WR: "???" (?)
|
Nachricht 0x85: DTU an WR: "???" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
GD->NRF 7E 15 70 51 43 68 70 51 43 68 85 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
GD->NRF 7E 15 70 51 43 68 70 51 43 68 85 xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
||||||
|
@ -202,9 +317,11 @@ GD->NRF 7E 15 70 51 43 68 70 51 43 68 85 xx 7F ..
|
||||||
on-air 15 70 51 43 68 70 53 54 53 85 BE
|
on-air 15 70 51 43 68 70 53 54 53 85 BE
|
||||||
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
||||||
WR ser # DTU ser #
|
WR ser # DTU ser #
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0xFF: DTU an WR: "???" (?)
|
Nachricht 0xFF: DTU an WR: "???" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
GD->NRF 7E 15 70 51 43 68 70 51 43 68 FF xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
GD->NRF 7E 15 70 51 43 68 70 51 43 68 FF xx 7F ...... (NOCH NICHT VERIFIZIERT / GESEHEN)
|
||||||
|
@ -216,9 +333,11 @@ GD->NRF 7E 15 70 51 43 68 70 51 43 68 FF xx 7F ..
|
||||||
on-air 15 70 51 43 68 70 53 54 53 FF C4
|
on-air 15 70 51 43 68 70 53 54 53 FF C4
|
||||||
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
(payload) ^^^^^^^^^^^ ^^^^^^^^^^^
|
||||||
WR ser # DTU ser #
|
WR ser # DTU ser #
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x01: WR an DTU: "Aktuelle DC Daten" (?)
|
Nachricht 0x01: WR an DTU: "Aktuelle DC Daten" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
7E 95 72 22 02 00 72 22 02 00 01 00 01 01 4c 03 bd 0c 46 00 b5 00 03 00 05 00 00 BD 7F
|
7E 95 72 22 02 00 72 22 02 00 01 00 01 01 4c 03 bd 0c 46 00 b5 00 03 00 05 00 00 BD 7F
|
||||||
|
@ -226,9 +345,11 @@ Nachricht 0x01: WR an DTU: "Aktuelle DC Daten" (?)
|
||||||
Bedeutung SOF MID WR ser# WR ser# CMD ? PV1.u PV1.i PV1.p PV2.u PV2.i PV2.p ? CRC8 EOF
|
Bedeutung SOF MID WR ser# WR ser# CMD ? PV1.u PV1.i PV1.p PV2.u PV2.i PV2.p ? CRC8 EOF
|
||||||
Einheit BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01A] [.1W] [0.1V] [0.01A] [.1W] ?
|
Einheit BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01A] [.1W] [0.1V] [0.01A] [.1W] ?
|
||||||
Beispiel 72220200 72220200 ? 33.2V 9.57A 317.2W 18.1V 0.03A 0.5W ?
|
Beispiel 72220200 72220200 ? 33.2V 9.57A 317.2W 18.1V 0.03A 0.5W ?
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x02: WR an DTU: "Aktuelle AC Daten" (?)
|
Nachricht 0x02: WR an DTU: "Aktuelle AC Daten" (?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
7E 95 72 22 02 00 72 22 02 00 02 28 23 00 00 24 44 00 3C 00 00 09 0F 13 88 0B D5 83 7F
|
7E 95 72 22 02 00 72 22 02 00 02 28 23 00 00 24 44 00 3C 00 00 09 0F 13 88 0B D5 83 7F
|
||||||
|
@ -236,9 +357,11 @@ Nachricht 0x02: WR an DTU: "Aktuelle AC Daten" (?)
|
||||||
Bedeutung SOF MID WR ser# WR ser# CMD ? ? ? AC.u AC.f AC.p CRC8 EOF
|
Bedeutung SOF MID WR ser# WR ser# CMD ? ? ? AC.u AC.f AC.p CRC8 EOF
|
||||||
Einheit BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01Hz] [0.1W]
|
Einheit BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01Hz] [0.1W]
|
||||||
Beispiel 72220200 72220200 ? 9284 60 231.9V 50.00Hz 302.9W
|
Beispiel 72220200 72220200 ? 9284 60 231.9V 50.00Hz 302.9W
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Nachricht 0x83: WR an DTU (?): "???" (nach CMD wäre das eher auch eine Antwort vom WR?)
|
Nachricht 0x83: WR an DTU (?): "???" (nach CMD wäre das eher auch eine Antwort vom WR?)
|
||||||
|
```
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
7E 95 72 22 02 00 72 22 02 00 83 00 03 00 83 03 E8 00 B2 00 0A FD 26 1E 7F
|
7E 95 72 22 02 00 72 22 02 00 83 00 03 00 83 03 E8 00 B2 00 0A FD 26 1E 7F
|
||||||
|
@ -246,25 +369,118 @@ Nachricht 0x83: WR an DTU (?): "???" (nach CMD wäre das eher auch eine Antwort
|
||||||
Bedeutung SOF MID WR ser# WR ser# CMD ? ? ? ? ? ? CRC8 EOF
|
Bedeutung SOF MID WR ser# WR ser# CMD ? ? ? ? ? ? CRC8 EOF
|
||||||
Einheit BCD (letzte 8) BCD (letzte 8) ?
|
Einheit BCD (letzte 8) BCD (letzte 8) ?
|
||||||
Beispiel 72220200 72220200 ? 131 1000 178 10
|
Beispiel 72220200 72220200 ? 131 1000 178 10
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Hinweise
|
Hinweise
|
||||||
========
|
--------
|
||||||
|
|
||||||
Die "on-air (payload)" Bytes geben nur die Nutzlast der gesendeten Shockburst-Pakete an.
|
Die "on-air (payload)" Bytes geben nur die Nutzlast der gesendeten Shockburst-Pakete an.
|
||||||
Intern enthalten diese Pakete auch die Zieladresse, die Länge, eine CRC.
|
Intern enthalten diese Pakete auch die Zieladresse, die Länge, eine CRC.
|
||||||
|
|
||||||
|
|
||||||
Legende
|
|
||||||
=======
|
|
||||||
|
|
||||||
MID: Message-ID. Antworten haben Bit 7 gesetzt,
|
*****************************************************************************************************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Enhanced Shockburst Payloads
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
- These are the packets that are exchanged between inverters and DTU via the Nordic
|
||||||
|
"Enhanced Shockburst" protocol.
|
||||||
|
- Each payload is preceded by a preamble, and terminated by a 16-bit CRC, as described
|
||||||
|
in the [Nordic datasheet][3]. See also figure 4 above.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
CMD 0x80: DTU --> WR: "Set time/date" (?)
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|<-------------CRC16 'modbus' für CRC_M----------------->|
|
||||||
|
15 72220200 72220200 80 0B 00 62 09 04 9b 00 00 00 00 00 00 00 00 F2 68 F0
|
||||||
|
^^ ^^^^^^^^ ^^^^^^^^ ^^ ^^^^^ ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^
|
||||||
|
Name MID DTU_SER# DTU_SER# CMD uk1 TIME (local) SEQ? CRC_M CRC8
|
||||||
|
Units see "addressing" ? [s-since-epoch] HI LO
|
||||||
|
Example 72220200 72220200 ? 2022-02-13
|
||||||
|
13:16:11
|
||||||
|
```
|
||||||
|
- This message will cause the inverter to transmit a CMD=0x01, CMD=0x02, and, occasionally, also a CMD=0x83 message
|
||||||
|
to the DTU with serial number DTU_SER#.
|
||||||
|
- Values of "0xb0, 0x00" and "0x11, 0x00" have been observed for "UK1". Their meaning is unknown.
|
||||||
|
- "SEQ" was observed to contain increasing numbers when sent by a Hoymiles DTU. In particular,
|
||||||
|
each issued "command" (e.g. "switch inverter on", "switch inverter off") appears to increase this
|
||||||
|
value. A constant value of 0x0000 or 0x0005 appears to work just fine.
|
||||||
|
- Repeatedly sending the same TIME information (instead of correctly increasing time)
|
||||||
|
[has been shown](https://www.mikrocontroller.net/topic/525778?page=2#7021386) to result
|
||||||
|
in identical behaviour, the inverter still replies as described above.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
CMD 0x01: WR --> DTU: "Current DC data" (?) (shown for an HM-700)
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
95 72 22 02 00 72 22 02 00 01 00 01 01 4c 03 bd 0c 46 00 b5 00 03 00 05 00 00 BD 7F
|
||||||
|
^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ ^^
|
||||||
|
NameMID WR ser# WR ser# CMD ? PV1.u PV1.i PV1.p PV2.u PV2.i PV2.p ? CRC8 EOF
|
||||||
|
Units BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01A] [.1W] [0.1V] [0.01A] [.1W] ?
|
||||||
|
Example 72220200 72220200 ? 33.2V 9.57A 317.2W 18.1V 0.03A 0.5W ?
|
||||||
|
```
|
||||||
|
- The exact meaning of the contents of this message varies depending on inverter type. So far, the following variants have been observed:
|
||||||
|
- HM-400 (single channel):
|
||||||
|
- HM-700 (2-channel):
|
||||||
|
- HM-1500 (4-channel):
|
||||||
|
```
|
||||||
|
TODO TODO TODO
|
||||||
|
73109025 73109025 01 00 01 014F 0003 000B 0000 40AE 03AC 08E6 7C
|
||||||
|
^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
335 3 11 940 2278
|
||||||
|
33.5V 0.03A 1.1W 940W 22.78kW
|
||||||
|
|
||||||
|
95 71603546 71603546 01 00 01 015D 004D 00B3 010C 0270 0001 3419 64 B327 B327 1
|
||||||
|
^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
349 77 179 1 13337
|
||||||
|
34.9V 0.77A 1.79W 1 133.37kW
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Nachricht 0x02: WR an DTU: "Aktuelle AC Daten" (?)
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
7E 95 72 22 02 00 72 22 02 00 02 28 23 00 00 24 44 00 3C 00 00 09 0F 13 88 0B D5 83
|
||||||
|
^^ ^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^
|
||||||
|
Bedeutung SOF MID WR ser# WR ser# CMD ? ? ? AC.u AC.f AC.p CRC8
|
||||||
|
Einheit BCD (letzte 8) BCD (letzte 8) ? [0.1V] [0.01Hz] [0.1W]
|
||||||
|
Beispiel 72220200 72220200 ? 9284 60 231.9V 50.00Hz 302.9W
|
||||||
|
```
|
||||||
|
- The exact meaning of the contents of this message varies depending on inverter type. So far, the following variants have been observed:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
```
|
||||||
|
Nachricht 0x83: WR an DTU (?): "???" (nach CMD wäre das eher auch eine Antwort vom WR?)
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
95 72 22 02 00 72 22 02 00 83 00 03 00 83 03 E8 00 B2 00 0A FD 26 1E
|
||||||
|
^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^
|
||||||
|
Bedeutung MID WR ser# WR ser# CMD ? ? ? ? ? ? CRC8
|
||||||
|
Einheit BCD (letzte 8) BCD (letzte 8) ?
|
||||||
|
Beispiel 72220200 72220200 ? 131 1000 178 10
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Legend
|
||||||
|
======
|
||||||
|
|
||||||
|
**MID**: Message-ID. Antworten haben Bit 7 gesetzt,
|
||||||
|
```
|
||||||
z.B. Frage 0x15 --> Antwort 0x95.
|
z.B. Frage 0x15 --> Antwort 0x95.
|
||||||
z.B. Frage 0x07 --> Antwort 0x87.
|
z.B. Frage 0x07 --> Antwort 0x87.
|
||||||
Für Kommunikation GD <--> NRF
|
Für Kommunikation GD <--> NRF
|
||||||
|
```
|
||||||
|
|
||||||
CMD:
|
**CMD**:
|
||||||
Befehl an den WR hat Bit 7 gesetzt
|
Befehl an den WR hat Bit 7 gesetzt
|
||||||
0x80 "Zeit setzen"
|
0x80 "Zeit setzen"
|
||||||
0x81 "Anfrage DC-Daten", erwartete Antwort: 0x01
|
0x81 "Anfrage DC-Daten", erwartete Antwort: 0x01
|
||||||
|
@ -276,33 +492,41 @@ CMD:
|
||||||
0x01 "Aktuelle DC-Daten"
|
0x01 "Aktuelle DC-Daten"
|
||||||
0x02 "Aktuelle AC-Daten"
|
0x02 "Aktuelle AC-Daten"
|
||||||
|
|
||||||
SOF: Start-of-Frame 0x7e
|
**SOF**: Start-of-Frame 0x7e
|
||||||
EOF: End-of-Frame 0x7f
|
|
||||||
CRC8: CRC8 mit poly=1 init=0 xor=0, für alle Bytes zwischen SOF und CRC8.
|
**EOF**: End-of-Frame 0x7f
|
||||||
|
|
||||||
|
**CRC8**: CRC8 mit poly=1 init=0 xor=0, für alle Bytes zwischen SOF und CRC8.
|
||||||
Beispiel in Python:
|
Beispiel in Python:
|
||||||
|
```
|
||||||
>>> import crcmod
|
>>> import crcmod
|
||||||
>>> f = crcmod.mkCrcFun(0x101, initCrc=0, xorOut=0)
|
>>> f = crcmod.mkCrcFun(0x101, initCrc=0, xorOut=0)
|
||||||
>>> payload = bytes((0x95,0x72,0x22,0x02,0x00,0x72,0x22,0x02,0x00,0x83,0x00,0x03,0x00,0x83,0x03,0xE8,0x00,0xB2,0x00,0x0A,0xFD,0x26))
|
>>> payload = bytes((0x95,0x72,0x22,0x02,0x00,0x72,0x22,0x02,0x00,0x83,0x00,0x03,0x00,0x83,0x03,0xE8,0x00,0xB2,0x00,0x0A,0xFD,0x26))
|
||||||
>>> hex(f(payload))
|
>>> hex(f(payload))
|
||||||
'0x1e'
|
'0x1e'
|
||||||
|
```
|
||||||
|
|
||||||
CRC_M: CRC16 wie für "Modbus"-Protokoll, High-Byte gefolgt von Low-Byte
|
**CRC_M**: CRC16 wie für "Modbus"-Protokoll, High-Byte gefolgt von Low-Byte
|
||||||
Beispiel in Python:
|
Beispiel in Python:
|
||||||
|
```
|
||||||
>>> import crcmod
|
>>> import crcmod
|
||||||
>>> f = crcmod.predefined.mkPredefinedCrcFun('modbus')
|
>>> f = crcmod.predefined.mkPredefinedCrcFun('modbus')
|
||||||
>>> payload = bytes((0x0B,0x00,0x62,0x2F,0x45,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00))
|
>>> payload = bytes((0x0B,0x00,0x62,0x2F,0x45,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00))
|
||||||
>>> hex(f(payload))
|
>>> hex(f(payload))
|
||||||
'0x3bd6'
|
'0x3bd6'
|
||||||
|
```
|
||||||
|
|
||||||
TIME: Aktuelle (DTU-)Zeit als Unix "time_t" (Sekunden seit 1970-01-01)
|
**TIME**: Aktuelle (DTU-)Zeit als Unix "time_t" (Sekunden seit 1970-01-01)
|
||||||
|
|
||||||
|
|
||||||
Glossar
|
Glossary
|
||||||
=======
|
========
|
||||||
|
|
||||||
WR: Wechselrichter
|
**WR**: Wechselrichter (inverter)
|
||||||
DTU: Data Terminal Unit (?). Die Hoymiles-Bezeichnung für den Kommunikations-Master.
|
|
||||||
BCD: Binary Coded Decimal
|
**DTU**: Data Terminal Unit (?). Die Hoymiles-Bezeichnung für den Kommunikations-Master.
|
||||||
|
|
||||||
|
**BCD**: Binary Coded Decimal
|
||||||
|
|
||||||
|
|
||||||
Notizen
|
Notizen
|
||||||
|
@ -315,12 +539,22 @@ Notizen
|
||||||
datetime.datetime.utcfromtimestamp(0x6209049b): datetime.datetime(2022, 2, 13, 13, 16, 11)
|
datetime.datetime.utcfromtimestamp(0x6209049b): datetime.datetime(2022, 2, 13, 13, 16, 11)
|
||||||
|
|
||||||
|
|
||||||
Historie
|
References
|
||||||
========
|
==========
|
||||||
|
|
||||||
|
- [1]: https://www.mikrocontroller.net/topic/525778 "The post that started the community effort"
|
||||||
|
- [2]: https://www.mikrocontroller.net "mikrocontroller.net"
|
||||||
|
- [3]: https://infocenter.nordicsemi.com/pdf/nRF24LE1_PS_v1.6.pdf "Nordic NRF24LE01+ datasheet"
|
||||||
|
- [4]: https://nrf24.github.io/RF24 "Optimized high speed nRF24L01+ driver documentation"
|
||||||
|
- [5]: https://github.com/grindylow/ahoy "AHOY Communications Project"
|
||||||
|
|
||||||
|
|
||||||
|
Revision History
|
||||||
|
================
|
||||||
|
|
||||||
2022-03-09 / Petersilie / erste Version
|
2022-03-09 / Petersilie / erste Version
|
||||||
2022-03-10 / Petersilie / r2 / Nachrichten "02 28 23" und "82 00 03" ergänzt. Sauberer ausgerichtet. Python Beispiel für CRC.
|
2022-03-10 / Petersilie / r2 / Nachrichten "02 28 23" und "82 00 03" ergänzt. Sauberer ausgerichtet. Python Beispiel für CRC.
|
||||||
2022-03-12 / Petersilie / r3 / Erste on-air Formate hinzu. CMD-IDs hinzu. Neue Nachrichten von arnaldo_g hinzu. Übersicht hinzu.
|
2022-03-12 / Petersilie / r3 / Erste on-air Formate hinzu. CMD-IDs hinzu. Neue Nachrichten von arnaldo_g hinzu. Übersicht hinzu.
|
||||||
2022-03-15 / Petersilie / r4 / Nachricht 0x80: Mystery-Bytes am Ende "dechiffriert"
|
2022-03-15 / Petersilie / r4 / Nachricht 0x80: Mystery-Bytes am Ende "dechiffriert"
|
||||||
2022-03-16 / Petersilie / r5 / ESP ist ein ESP8266, nicht ESP32 (danke an @tbnobody)
|
2022-03-16 / Petersilie / r5 / ESP ist ein ESP8266, nicht ESP32 (danke an @tbnobody)
|
||||||
2022-03-27 / Petersilie / Versionierung ab jetzt via Github.
|
2022-03-27 / Petersilie / all future revisions are now versioned via Git.
|
||||||
|
|
9
tools/rpi/ahoy.conf.example
Normal file
9
tools/rpi/ahoy.conf.example
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[mqtt]
|
||||||
|
host = 192.168.84.2
|
||||||
|
port = 1883
|
||||||
|
|
||||||
|
[dtu]
|
||||||
|
serial = 99978563412
|
||||||
|
|
||||||
|
[inverter]
|
||||||
|
serial = 444473104619
|
|
@ -25,11 +25,10 @@ mqtt_client.connect(mqtt_host, mqtt_port)
|
||||||
mqtt_client.loop_start()
|
mqtt_client.loop_start()
|
||||||
|
|
||||||
# Master Address ('DTU')
|
# Master Address ('DTU')
|
||||||
dtu_ser = 99978563412 # identical to fc22's
|
dtu_ser = cfg.get('dtu', 'serial', fallback='99978563412') # identical to fc22's
|
||||||
|
|
||||||
# inverter serial numbers
|
# inverter serial numbers
|
||||||
#inv_ser = 444473104619 # identical to fc22's #99972220200
|
inv_ser = cfg.get('inverter', 'serial', fallback='444473104619') # my inverter
|
||||||
inv_ser = 114174608145 # my inverter
|
|
||||||
|
|
||||||
# all inverters
|
# all inverters
|
||||||
#...
|
#...
|
||||||
|
@ -177,18 +176,38 @@ def on_receive(p, ch_rx=None, ch_tx=None):
|
||||||
d['wday2_Wh'] = uk5
|
d['wday2_Wh'] = uk5
|
||||||
d['uk2'] = uk2
|
d['uk2'] = uk2
|
||||||
|
|
||||||
elif cmd==0x83:
|
elif cmd==129:
|
||||||
name = 'misc1'
|
name = 'error'
|
||||||
uk1, uk2, uk3, uk4, uk5, uk6 = struct.unpack(
|
print('Command error')
|
||||||
'>HHHHHH', p[10:22])
|
|
||||||
print('')
|
elif cmd==131: # 0x83
|
||||||
|
name = 'statedata'
|
||||||
|
uk1, l, uk3, t, uk5, uk6 = struct.unpack('>HHHHHH', p[10:22])
|
||||||
|
print(f'l={l}%, t={t/10:.2f}C, ', end='')
|
||||||
|
print(f'uk1={uk1}, ', end='')
|
||||||
|
print(f'uk3={uk3}, ', end='')
|
||||||
|
print(f'uk5={uk5}, ', end='')
|
||||||
|
print(f'uk6={uk6}')
|
||||||
|
d['l_Pct'] = l
|
||||||
|
d['t_C'] = t/10
|
||||||
d['uk1'] = uk1
|
d['uk1'] = uk1
|
||||||
d['uk2'] = uk2
|
|
||||||
d['uk3'] = uk3
|
d['uk3'] = uk3
|
||||||
d['uk4'] = uk4
|
|
||||||
d['uk5'] = uk5
|
d['uk5'] = uk5
|
||||||
d['uk6'] = uk6
|
d['uk6'] = uk6
|
||||||
|
|
||||||
|
elif cmd==132: # 0x84
|
||||||
|
name = 'unknown0x84'
|
||||||
|
uk1, uk2, uk3, uk4, uk5, uk6, uk7, uk8 = struct.unpack(
|
||||||
|
'>HHHHHHHH', p[10:26])
|
||||||
|
print(f'uk1={uk1}, ', end='')
|
||||||
|
print(f'uk2={uk2}, ', end='')
|
||||||
|
print(f'uk3={uk3}, ', end='')
|
||||||
|
print(f'uk4={uk4}, ', end='')
|
||||||
|
print(f'uk5={uk5}, ', end='')
|
||||||
|
print(f'uk6={uk6}, ', end='')
|
||||||
|
print(f'uk7={uk7}, ', end='')
|
||||||
|
print(f'uk8={uk8}')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f'unknown cmd {cmd}')
|
print(f'unknown cmd {cmd}')
|
||||||
else:
|
else:
|
||||||
|
@ -203,11 +222,20 @@ def on_receive(p, ch_rx=None, ch_tx=None):
|
||||||
j = json.dumps(d)
|
j = json.dumps(d)
|
||||||
mqtt_client.publish(f'ahoy/{src}/{name}', j)
|
mqtt_client.publish(f'ahoy/{src}/{name}', j)
|
||||||
if d['cmd']==2:
|
if d['cmd']==2:
|
||||||
mqtt_client.publish(f'ahoy/{src}/{name}/p_W', d['p_W'])
|
mqtt_client.publish(f'ahoy/{src}/emeter/0/voltage', d['u_V'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/emeter/0/power', d['p_W'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/emeter/0/total', d['wtot1_Wh'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/frequency', d['f_Hz'])
|
||||||
if d['cmd']==1:
|
if d['cmd']==1:
|
||||||
mqtt_client.publish(f'ahoy/{src}/{name}/p1_W', d['p1_W'])
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/power', d['p1_W'])
|
||||||
mqtt_client.publish(f'ahoy/{src}/{name}/p2_W', d['p2_W'])
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/voltage', d['u1_V'])
|
||||||
mqtt_client.publish(f'ahoy/{src}/{name}/p_W', d['p1_W']+d['p2_W'])
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/current', d['i1_A'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/power', d['p2_W'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/voltage', d['u2_V'])
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/current', d['i2_A'])
|
||||||
|
if d['cmd']==131:
|
||||||
|
mqtt_client.publish(f'ahoy/{src}/temperature', d['t_C'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main_loop():
|
def main_loop():
|
||||||
|
@ -222,57 +250,78 @@ def main_loop():
|
||||||
print_addr(dtu_ser)
|
print_addr(dtu_ser)
|
||||||
|
|
||||||
ctr = 1
|
ctr = 1
|
||||||
|
last_tx_message = ''
|
||||||
|
|
||||||
ts = int(time.time()) # see what happens if we always send one and the same (constant) time!
|
ts = int(time.time()) # see what happens if we always send one and the same (constant) time!
|
||||||
ch_tx = 40
|
|
||||||
ch_rx = 3
|
rx_channels = [3,23,61,75]
|
||||||
|
rx_channel_id = 0
|
||||||
|
rx_channel = rx_channels[rx_channel_id]
|
||||||
|
|
||||||
|
tx_channels = [40]
|
||||||
|
tx_channel_id = 0
|
||||||
|
tx_channel = tx_channels[tx_channel_id]
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
radio.setChannel(ch_rx)
|
# Sweep receive start channel
|
||||||
|
rx_channel_id = ctr % len(rx_channels)
|
||||||
|
rx_channel = rx_channels[rx_channel_id]
|
||||||
|
|
||||||
|
radio.setChannel(rx_channel)
|
||||||
radio.enableDynamicPayloads()
|
radio.enableDynamicPayloads()
|
||||||
radio.setAutoAck(False)
|
radio.setAutoAck(True)
|
||||||
radio.setPALevel(RF24_PA_MAX)
|
radio.setPALevel(RF24_PA_MAX)
|
||||||
radio.setDataRate(RF24_250KBPS)
|
radio.setDataRate(RF24_250KBPS)
|
||||||
radio.openWritingPipe(ser_to_esb_addr(inv_ser))
|
radio.openWritingPipe(ser_to_esb_addr(inv_ser))
|
||||||
radio.flush_rx()
|
radio.flush_rx()
|
||||||
radio.flush_tx()
|
radio.flush_tx()
|
||||||
radio.openReadingPipe(1,ser_to_esb_addr(dtu_ser))
|
radio.openReadingPipe(1,ser_to_esb_addr(dtu_ser))
|
||||||
#radio.openReadingPipe(1,ser_to_esb_addr(inv_ser))
|
|
||||||
radio.startListening()
|
radio.startListening()
|
||||||
|
|
||||||
if ctr<3:
|
|
||||||
pass
|
|
||||||
# radio.printPrettyDetails()
|
|
||||||
|
|
||||||
t_end = time.monotonic_ns()+1e9
|
t_end = time.monotonic_ns()+1e9
|
||||||
while time.monotonic_ns() < t_end:
|
while time.monotonic_ns() < t_end:
|
||||||
has_payload, pipe_number = radio.available_pipe()
|
has_payload, pipe_number = radio.available_pipe()
|
||||||
if has_payload:
|
if has_payload:
|
||||||
size = radio.getDynamicPayloadSize()
|
size = radio.getDynamicPayloadSize()
|
||||||
payload = radio.read(size)
|
payload = radio.read(size)
|
||||||
print(f"Received {size} bytes on pipe {pipe_number}: " +
|
print(last_tx_message, end='')
|
||||||
|
last_tx_message = ''
|
||||||
|
dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||||
|
print(f"{dt} Received {size} bytes on channel {rx_channel} pipe {pipe_number}: " +
|
||||||
" ".join([f"{b:02x}" for b in payload]))
|
" ".join([f"{b:02x}" for b in payload]))
|
||||||
on_receive(payload, ch_rx=ch_rx, ch_tx=ch_tx)
|
on_receive(payload, ch_rx=rx_channel, ch_tx=tx_channel)
|
||||||
else:
|
else:
|
||||||
pass
|
# pass
|
||||||
# time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
|
radio.stopListening()
|
||||||
|
radio.setChannel(rx_channel)
|
||||||
|
radio.startListening()
|
||||||
|
rx_channel_id = rx_channel_id + 1
|
||||||
|
if rx_channel_id >= len(rx_channels):
|
||||||
|
rx_channel_id = 0
|
||||||
|
rx_channel = rx_channels[rx_channel_id]
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
tx_channel_id = tx_channel_id + 1
|
||||||
|
if tx_channel_id >= len(tx_channels):
|
||||||
|
tx_channel_id = 0
|
||||||
|
tx_channel = tx_channels[tx_channel_id]
|
||||||
|
|
||||||
radio.stopListening() # put radio in TX mode
|
radio.stopListening() # put radio in TX mode
|
||||||
radio.setChannel(ch_tx)
|
radio.setChannel(tx_channel)
|
||||||
radio.openWritingPipe(ser_to_esb_addr(inv_ser))
|
radio.openWritingPipe(ser_to_esb_addr(inv_ser))
|
||||||
|
|
||||||
if ctr<3:
|
ts = int(time.time())
|
||||||
pass
|
|
||||||
# radio.printPrettyDetails()
|
|
||||||
|
|
||||||
# ts = int(time.time())
|
|
||||||
payload = compose_0x80_msg(src_ser_no=dtu_ser, dst_ser_no=inv_ser, ts=ts)
|
payload = compose_0x80_msg(src_ser_no=dtu_ser, dst_ser_no=inv_ser, ts=ts)
|
||||||
print(f"{ctr:5d}: len={len(payload)} | " + " ".join([f"{b:02x}" for b in payload]),
|
dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||||
flush=True)
|
last_tx_message = f"{dt} Transmit {ctr:5d}: channel={tx_channel} len={len(payload)} | " + \
|
||||||
|
" ".join([f"{b:02x}" for b in payload]) + "\n"
|
||||||
radio.write(payload) # will always yield 'True' because auto-ack is disabled
|
radio.write(payload) # will always yield 'True' because auto-ack is disabled
|
||||||
t_last_tx = time.monotonic_ns()
|
t_last_tx = time.monotonic_ns()
|
||||||
ctr = ctr + 1
|
ctr = ctr + 1
|
||||||
|
|
||||||
|
print(flush=True, end='')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
paho-mqtt
|
paho-mqtt
|
||||||
|
crcmod
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue