Open broth-itk opened 9 months ago
Promiscuous mode would be possible as you can set both DTU and inverter Serial ID on the current menus. Those could be added both/all to the maximum five NRF pipes.
Difficulty would be to guess the channels used by both Hoymiles DTU and inverter. But switching between the five NRF channels might be sufficient as the messages/packets are usually repeated 15 times/transmitted 16 times. So switching channel after 2-4ms may be sufficient, especially if configurable.
Payload parsing may not be needed in the first place. Though that and a pass-through repeater mode may be added at a later stage ;)
I don't like the idea of channel hopping for captures. As OpenDTU has the same inverters configured as the Hoymiles DTU, we can scan all channels until we see active communication with matching serials. That channel is locked in and dumped to syslog.
The typical WR <-> DTU messages are repeated by the NRF radio 15 times, i.e. 16 packets as you can see in the following pictures.
One packet with 15 repeats takes about ~35 ms to be sent, i.e. a single packet will take about 2,5-3 ms with a ~1 ms gap between repeated packets. (The larger gaps between consecutive packets / messages sent from the WR -> DTU are usually about 9-10 ms each.)
So switching the five channels after 2-5 ms listening on each should allow you to listen on each channel 2-3 times during the complete transmission period for a single packet with all its retransmits. And also the inverter is changing the channel from time to time, which has been known as channel hopping.
Please note that it may be easier (and has been done initially with the Hoymiles DTU Lite too) to simply hook two serial2usb converters onto the RX/TX pins of an original Hoymiles DTU: BAUD 125000, 8N1 should be the correct settings for the UART communication between the main cpu and the RF chips.
See https://github.com/tbnobody/OpenDTU/issues/1540#issuecomment-1885707736
@stefan123t OK, since I have an original DTU, it might be easier to make both signals available from the outside to capture data.
As long I don't have to open the lid of the transceiver, all is fine :)
@stefan123t
Here you have it, my new HMDUSI - HoyMiles Dual USB Sniffer Interface
Now I need to do some software to capture data with exact time stamps.
Do you happen to know something which can be used?
I think capturing the data should be very straightforward with a simple python script. If you run that twice in two separate terminal windows, I think you're good.
Make sure pyserial is installed: pip install pyserial
import serial
import datetime
# Replace baud_rate with the actual Hoymiles baud rate
def read_serial(port, baud_rate=9600, log_location='log.txt'):
try:
ser = serial.Serial(port, baud_rate, timeout=1)
print(f"Connected to {port}")
try:
with open(log_location, "a") as file:
while True:
if ser.in_waiting > 0:
data = ser.readline().decode('utf-8').rstrip()
timestamp = datetime.datetime.now()
log_entry = f"{timestamp}: {data}\n"
print(log_entry)
file.write(log_entry)
except KeyboardInterrupt:
print("Interrupted by user, exiting.")
except Exception as e:
print(f"Error: {e}")
except serial.SerialException as e:
print(f"Serial error: {e}")
finally:
try:
ser.close()
print("Serial port closed.")
except:
print("Error closing serial port.")
# Replace with your port, e.g., 'COM3' for Windows, '/dev/ttyUSB0' for Linux/macOS
read_serial('/dev/tty.usbserial-0001', 9600, "log_1.txt")
Use Ctrl + C
to interrupt and close the script.
Make sure NOT to call the script serial.py
, as that will cause conflicts with the serial library. Call it something else instead like serial_log.py
.
According to other users info the BAUD rate is 125000 and 8N1 for parity, start/stop bits.
The Grid Profile update has been traced https://github.com/tbnobody/OpenDTU/issues/900#issuecomment-1899406249 by now, but you can still contribute the firmware update if you want. Eventuall they both use the 0x0A DOWN_DAT main command, though that ist just a quick guess.
To get a firmware dump you would need access to the JTAG/SWD port on the inverter. Some other users do have that at the moment and have contributed a firmware dump already.
I modified the code a bit to provide a more or less usable output. Attached some files with the outputs (oringial towards this final version)-
Before doing any more steps with the DTU and updates, can you please take a look to the dumps and check if this is usable at all?
Thanks!
#!/usr/bin/python3
import serial
import datetime
import binascii
# Replace baud_rate with the actual Hoymiles baud rate
def read_serial(port, baud_rate=9600, log_location='log.txt'):
try:
ser = serial.Serial(port, baud_rate, timeout=1)
print(f"Connected to {port}")
try:
with open(log_location, "a") as file:
while True:
if ser.in_waiting > 0:
data = ser.read(ser.in_waiting)
h = binascii.hexlify(data)
timestamp = datetime.datetime.now()
log_entry = f"A: {timestamp}: {h}\n"
print(log_entry)
file.write(log_entry)
file.flush()
except KeyboardInterrupt:
print("Interrupted by user, exiting.")
except Exception as e:
print(f"Error: {e}")
except serial.SerialException as e:
print(f"Serial error: {e}")
finally:
try:
ser.close()
print("Serial port closed.")
except:
print("Error closing serial port.")
# Replace with your port, e.g., 'COM3' for Windows, '/dev/ttyUSB0' for Linux/macOS
read_serial('/dev/ttyUSB0', 125000, "log_1.txt")
Nice that you got it working! I'm glad I could contribute this small bit. I'm unfortunately not the right person to answer your question, but hopefully this will get us closer to supporting firmware updates and setting grid profiles.
PS: If you add the file extension to the start of a markdown code block, you enable syntax highlighting. :)
```py
Thanks for your hint, slways something new to learn ;-)
I will keep the logging running for a while. Did "grid profile update" already and some other maintenance tasks. Maybe we catch something of interest here.
Here we go, these logs were collected during 5 to 6 hours until night came to send the inverters asleep.
I can't tell which of these logs are RX or TX but following log has above combined and sorted by time:
This should make analysis easier. Maybe someone can spot a new command or something we haven't found yet.
I'll detach the DTU and use OpenDTU instead now.
Any feedback about the logs? Are the captures usable?
I plan to perform firmware updates on all inverters on friday.
Hi @broth-itk, I did not have time to look into the logs in detail. The hex format is a bit difficult to read, so I will have to post-process them to understand what is going on. Before that I can not say what the content / commands are that are used in the logs.
A: 2024-01-28 11:46:54.330986: b'~\xd6\x80\x16F3\x80\x16F3\x01\x00\x15!\xe3\x7f~\xd6\x80\x16F3\x80\x16F3\x01\x00\x15!\xe3\x7f~\xd6\x80\x16F3\x80\x16F3\x01\x00\x15!\xe3\x7f~\xd6\x80\x16F3\x80\x16F3\x01\x00\x15!\xe3\x7f'
...
A: 2024-01-28 11:53:58.630405: b'7ed6801646338016463301001521e37f'
B: 2024-01-28 11:53:58.684149: b'7e5683199047831990470215212114557f'
B: 2024-01-28 11:53:58.828190: b'7e5683199047831990470215212114557f'
B: 2024-01-28 11:53:58.988290: b'7e5683199047831990470215212114557f'
A: 2024-01-28 11:53:59.078706: b'7ed6801646338016463301001521e37f'
B: 2024-01-28 11:53:59.084321: b'7e5680164633801646330115212114567f'
B: 2024-01-28 11:53:59.132321: b'7e5680164633801646330115212114567f'
A: 2024-01-28 11:53:59.142698: b'7ed6801646338016463301001521e37f'
B: 2024-01-28 11:53:59.196374: b'7e5683199047831990470215212114557f'
B: 2024-01-28 11:53:59.340453: b'7e5683199047831990470215212114557f'
B: 2024-01-28 11:53:59.484530: b'7e5683199047831990470215212114557f'
A: 2024-01-28 11:53:59.591014: b'7ed6801646338016463301001521e37f'
B: 2024-01-28 11:53:59.596584: b'7e5680164633801646330115212114567f'
B: 2024-01-28 11:53:59.644618: b'7e158319904783199047800b0065b6437c000000000000000015a7c07f'
B: 2024-01-28 11:54:00.092834: b'7e15831990478319904780120065b6437c0000000000000000ccbf187f'
B: 2024-01-28 11:54:00.845265: b'7e15813130708131307080120065b6437d5d00000000000000005cb2847f'
A: 2024-01-28 11:54:01.592603: b'7ed6801646338016463301001521e37f'
B: 2024-01-28 11:54:01.597569: b'7e5680164633801646330115212114567f'
B: 2024-01-28 11:54:01.645613: b'7e5683199047831990470215212114557f'
B: 2024-01-28 11:54:01.789726: b'7e5683199047'
B: 2024-01-28 11:54:01.805766: b'831990470215212114557f'
I just looked into the combined output again and found that after several minutes of the \xd6
chatter the signal improves and I can actually read part of the data being sent.
The last two lines are obviously separated, though they belong to the same frame starting with 7e
and ending with 7f
.
The SPI communication uses 7e
SOF (Start of Frame) and 7f
EOF (End of Frame) and some escape mechanism to replace these two bytes in case they occur otherwise in the data.
Given the above communication B: are the TX messages from the DTU to the inverter (e.g. Main command 0x56
)
whereas A: are the RX responses from the inverter to the DTU (e.g. reply to command 0x56 | 0x80 = 0xd6
).
Thanks for your feedback! Well, the proposed python script gave weird outputs and I don't think the DTU is using UTF-8 to communicate to its radio module. The script can still be improved by
But as time is precious, if the collected dumps are complete, postprocessing can always be done.
Here you go, these logs include the firmware update of two -4T inverters to 1.0.27 (Firmware Build Date 2023-06-05 10:24:00):
@broth-itk I have downloaded them but I will not be able to analyse them in the coming weeks. But many thanks for making the traces, I am already excited to learn some new "commands" which we can add as a new feature to OpenDTU/AhoyDTU at a later stage.
@stefan123t Thanks! The logs contain all data since startup of the DTU. Maybe there is something interesting - indeed. Good luck! Cheers!
Huch ich sollte mal die Logs vom Firmware Update von @broth-itk auswerten und wie versprochen dokumentieren.
@broth-itk ich habe das log_1.txt ein wenig aufgeräumt, die Kommandos auf dem UART / der Seriellen Schnittstelle zwischen MCU und NRF / CMT Modul beginnen immer mit 7e
und enden auf 7f
, wenn das nicht der Fall ist, dann wurde beim Logging etwas umgebrochen, was nicht sein muß / sein sollte. Ich habe den folgenden Regex dazu verwendet (?<!7f)'\n[AB]: [0-9-]{10} [0-9:.]{15}: b'(?!7e)
, vielleicht willst Du das noch in Dein Python Script einarbeiten ?
@stefan123t ist das tatsächlich so, dass die Frames stets mit 7e beginnen und mit 7f enden? Ist sichergestellt, dass ein 7e innerhalb eines Pakets nicht auftritt ;-)
Das Script kann ich gerne anpassen und demnächst einen neuen Trace (wegen Power Distribution Logic) ziehen. Das wird sicherlich einige interessieren und wir sollten sicherstellen, dass die Tracedaten vollständig und verwendbar sind.
Hallo Bernhard @broth-itk ja die 7e/7f werden durch 7d5e/7d5f escaped, 7d selbst durch 7d5d. Ich habe es hier detailliert beschrieben: https://github.com/tbnobody/OpenDTU/issues/1421#issuecomment-2425232860
Danke @stefan123t, habe das Script angepasst und werde das morgen tagsüber ausprobieren. Bevor ich das Firmware/Grid Profile Update laufen lasse, würde ich Dir einen kurzen Trace zur Verfügung stellen. Nicht, dass am Ende die Traces nicht passen :)
@broth-itk Danke die Traces passen prinzipiell schon so wie sie jetzt sind. Die Umbrüche und Ersetzungen der Escapes kann man ja leicht auch nachträglich machen. Ich habe lediglich für die einfachere Lesbarkeit ein paar Leerzeichen eingefügt.
Ich habe das Script so erweitert, dass nun alles in einer Datei (Timestamp, Hex-Dump), nach Frames getrennt und mit Prefix RX/TX versehen ist. So ist es für den menschlichen Betrachter besser sichtbar und der zeitliche Zusammenhang erkennbar. Das davor war eher Q&D
So sieht das nun aus, denke damit kann man etwas besser arbeiten:
Die Frames werden einzeln aus dem Datenstrom herausgesucht und ins Log geschrieben. Anstelle von RX/TX habe ich schlicht << und >> ins Log geschrieben da ich schlicht nicht weiß welcher USB/Serial Adapter auf welcher Leitung hängt. Sollte aber so passen.
Ich lasse die DTU morgen mal den ganzen Tag online und beobachte.
@broth-itk ja sieht gut aus die Richtung passt.
Z.B. das MainCmd 0x15 REQ_ARW_DAT_ALL mit dem SubCmd RealTimeRunData_Debug [sic] 0x0B ist die Anfrage nach den aktuellen Werten. Die geht von der DTU zum WR >>.
Die Antwort wäre dann 0x95 (immer MainCmd | 0x80) vom WR zur DTU <<.
Die DTU stellt aber nebenher auch ganz viele 0x56 Anfragen und bekommt 0xD6 Antworten zurück.
Weiß jemand zufällig wie das 0x56 genau aufgebaut ist ? Es ist wohl das ChannelChangeCommand bei den HMS/HMT mit CMT2300A RF Modul.
Siehe auch hier die Dokumentation im Source Code: https://github.com/tbnobody/OpenDTU/blob/dc5eb96f5035ba9e058e38d83df2d6c691b0764b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp#L9-L18
Die Diskussion zum ChannelChangeCommand sollten wir m.E. hier weiterführen: https://github.com/tbnobody/OpenDTU/issues/2137#issuecomment-2427788809
@stefan123t
Anbei ein großer Trace von heute morgen. Darin sind folgende Updates enthalten:
@stefan123t Nach dem update meines 1600 bekomme ich keine Verbindung mehr mit der Opendtu. Wele FW der opendtu hast du?
Update nachdem ich beide Geräte einmal vom Strom getrennt habe und sie wieder angeschlossen habe hat es nach einer knappen halben Stunde wieder funktioniert sie haben sich synchronisiert und jetzt läuft es wieder
@AlexJacu bitte ein eigenes Issue aufmachen oder Deinen Kommentar löschen. Hier geht es nur um die Implementierung / Dokumentation des Sniffer Modus. Danke.
Is your feature request related to a problem? Please describe.
OpenDTU team want captures of firmware upgrades of the inverters.
Describe the solution you'd like
Switch OpenDTU in "sniffer" mode to listen, receive and store all communication from Hoymiles DTU to the inverters.
The captured data should be send over the network to syslog server for further analysis.
Wishlist:
Once we have (unencrypted?) firmware dumps of the inverters, we can start disassembling ;-)
Comments are welcome. Thanks!
Describe alternatives you've considered
No response
Additional context
No response