Firmware: MW4_01_5429_SS_04_00.00.00.0B (2025-02-06)
SoC: BL602 RISC-V, flash base 0x23000000
Decompiled source: MW4_01_5429_SS_04_decompressed.bin.c
The cloud can send AT commands directly to the device. The path is:
Cloud TCP socket (YZSSL task)
→ sub_2309FA8A(data, len)
→ sub_2309F90A(0, data, len, outbuf, 0x400, &outlen)
→ checks for 'A','T','+' prefix
→ sub_2309F81E(cmd_name) → looks up command in table at off_230EF564
→ calls handler(a1, num_args-1, arg_array, outbuf, buflen)
AT command struct format (4 words each):
[0] = char* cmd_name
[1] = handler_fn* (both GET and SET in one function)
[2] = char* help_string
[3] = NULL (terminator / unused)
The handler receives x11 = len (0 for GET, >0 for SET) and x12 = ptr to argument array.
There are three independent dispatch paths → all served on the same TCP port 48899:
| Path | Dispatcher | Table | Commands |
|---|---|---|---|
| 1 | sub_2309F90A → sub_2309F81E |
off_230EF564 (flash, 16 B/entry) |
YZ custom commands (YZCATTIME, YZEXPFUN, etc.) |
| 2 | sub_2300E016 → sub_2300DEB6 → sub_2300A928 |
RAM list at 0x4200D1F4 (2 sub-tables) |
HiFlying standard commands (NETP, UART, WMODE, etc.) |
| 3 | Direct sub_230C7CFE string matching |
No table → inline if/else chain | One-off commands (WEBU, AT+Z, BLEADV, WALK, etc.) |
Table starts at off_230EF564 (0x230ef564). Each entry is 16 bytes.
| Entry VA | Command | Handler VA | Role |
|---|---|---|---|
0x230ef564 |
TIME |
0x230a0bf6 |
Set/Get device RTC time |
0x230ef574 |
ADDRESS |
0x230a0686 |
Set/Get Modbus slave address |
0x230ef584 |
KEY |
0x230a0cc4 |
Set/Get device access key |
0x230ef594 |
MTYPE |
0x230a0d50 |
Set/Get device model type |
0x230ef5a4 |
CNMOSAVE |
0x230a0dd6 |
Save config to NVRAM |
0x230ef5b4 |
DEVICENUM |
0x230a1016 |
Set/Get number of connected devices |
0x230ef5c4 |
DEVSELCTL |
0x230a0f58 |
Device selection control |
0x230ef5d4 |
NDBGS |
0x230a109a |
Debug settings |
0x230ef5e4 |
FFCLEAR |
0x230a1120 |
Flash/file clear |
0x230ef5f4 |
LANGSEL |
0x230a11c0 |
Set/Get language selection |
0x230ef604 |
ADTIME |
0x2309f16e |
Set/Get device absolute time |
0x230ef614 |
YZFTIME |
0x230a1670 |
Set/Get factory time |
0x230ef624 |
YZEARSE |
0x230a0292 |
Erase flash data |
0x230ef634 |
YZACK |
0x230a1704 |
Acknowledgement command |
0x230ef644 |
YZTIME |
0x230a0cc0 |
Set/Get YZ time |
0x230ef654 |
WKTIME |
0x2309f214 |
Get current time from RTC (GET only; same body as ADTIME) |
0x230ef664 |
ERRCLR |
0x2309ecf2 |
Clear error flags |
0x230ef674 |
YZDBNUM |
0x230a18f6 |
DB entry count |
0x230ef684 |
YZDB |
0x230a1ac0 |
DB access |
0x230ef694 |
YZVER |
0x2309ea14 |
Get firmware version |
0x230ef6a4 |
YZLOG |
0x2309f2ae |
Get log data |
0x230ef6b4 |
YZAPP |
0x2309ee46 |
App control |
0x230ef6c4 |
APSTAT |
0x230a1c22 |
AP status |
0x230ef6d4 |
YZSIZE |
0x2309eb64 |
Flash file sizes |
0x230ef6e4 |
INVDATA |
0x2309f3da |
Inverter raw data |
0x230ef6f4 |
WEBUPURL |
0x230a31a2 |
OTA download URL |
0x230ef704 |
GLTYPE |
0x2309e92e |
Get logger type |
0x230ef714 |
OFFLINENUM |
0x2309eab8 |
Offline record count |
0x230ef724 |
APSOCKET |
0x230a1234 |
AP socket config |
0x230ef734 |
YZWMODE |
0x230a12b2 |
WiFi mode |
0x230ef744 |
YZWAKEYCTL |
0x230a1330 |
WiFi wake key control |
0x230ef754 |
VER |
0x2309eb06 |
Version string |
0x230ef764 |
YZFLSHTEST |
0x2309f694 |
Flash integrity test |
0x230ef774 |
YZRSTTIME |
0x230a1cba |
Set/Get reset timers |
0x230ef784 |
YZBVER |
0x2309e932 |
Bootloader version |
0x230ef794 |
YZFACTEST |
0x2309ebfa |
Factory test |
0x230ef7a4 |
YZPLAN |
0x230a11c0 |
Set/Get plan/schedule digit (0→9) |
0x230ef7b4 |
YZFLASHREAD |
0x2309ec7e |
Read flash sector |
0x230ef7c4 |
YZCATTIME |
0x230a13b8 |
Get upload period byte |
0x230ef7d4 |
YZEXPFUN |
0x230a1436 |
Set upload period byte (2-char decimal) |
0x230ef7e4 |
IVGET |
0x2309f75e |
Inverter data get |
0x230ef7f4 |
YZNETCHECK |
0x230a1598 |
Network health check |
0x230ef804 |
YZPCHECK |
0x230a1604 |
Protocol check |
0x230ef814 |
DTYPE |
0x2309ea62 |
Device type |
0x230ef824 |
IFILE |
0x230a3bf0 |
Internal file access |
0x230ef834 |
HTTPPOSTFILE |
0x230a46b6 |
HTTP POST file upload |
0x230ef844 |
HTTPPOST |
0x230a0510 |
HTTP POST data |
0x230ef854 |
FILEGET |
0x2309eeb2 |
File download |
0x230ef864 |
SDTT |
0x2309f4fa |
SD test/telemetry |
0x230ef874 |
SERCH |
0x230a1eb6 |
Server/host search |
0x230ef884 |
SSLSER |
0x230a17dc |
SSL server config |
0x230ef894 |
YZWSKEYPSD |
0x230a00fc |
WiFi key/password |
0x230ef8a4 |
MQTTSER |
0x230a17dc |
MQTT server (shares handler with SSLSER) |
0x230ef8b4 |
YZSYSVER |
0x230a1d5a |
System version |
0x230ef8c4 |
YZSYSCON |
0x230a1dce |
System connection config |
0x230ef8d4 |
YZSYSKEY |
0x230a0378 |
System key |
0x230ef8e4 |
YZDEFC |
0x230a1e42 |
Default config |
0x230ef8f4 |
TS_SN |
0x230a14b4 |
Set/Get TS serial number |
0x230ef904 |
YZFMODE |
0x230a47cc |
File/flash mode |
0x230ef914 |
YZFNL |
0x230a1528 |
File NL setting |
0x230ef924 |
YZDEVCTIME |
0x230a199e |
Set/Get device timing params |
0x230ef934 |
YZSPEEDREAD |
0x230a1a48 |
Speed read |
0x230ef944 |
WEBCTL |
0x230a1f1c |
Web control |
0x230ef954 |
SKA |
0x230a1f9a |
SKA auth |
Registered at boot via sub_23013F4C(). No password required for any of these.
| Command | GET Response | SET Format | Notes |
|---|---|---|---|
AT+NETP |
=TCP,Server,<port>,<ip> |
AT+NETP=TCP,Server,<port>,<ip> |
Remote server connection params |
AT+NSTAT |
=Weak/Normal/Good,NN% or =Disconnected |
N/A | WiFi signal quality |
AT+TCPDISB |
current TCP state | AT+TCPDISB=on/off |
Disable TCP |
AT+TCPLK |
=on/=off |
GET-only | SOCKB link status |
AT+UDPLK |
=on/=off |
GET-only | SOCKA UDP status |
AT+NTPSER |
NTP hostname | AT+NTPSER=<host> |
Max 32 chars |
AT+DHCPSR |
=DHCP,ip,mask,gw |
AT+DHCPSR=DHCP or =STATIC,ip,mask,gw |
DHCP/static config |
AT+TCPTO |
=<seconds> |
AT+TCPTO=<0-600> |
TCP timeout |
AT+SOCKB |
=NONE/TCP/UDP/UDPS,port,ip |
AT+SOCKB=<proto>,<port>,<ip> |
Secondary socket config |
AT+TCPCS |
=on/=off |
AT+TCPCS=on/off |
SOCKA connection state |
AT+TCPB_TO |
=<seconds> |
AT+TCPB_TO=<0-600> |
SOCKB timeout |
AT+UARTTM |
=<ms> |
AT+UARTTM=<32-1368> |
UART frame timeout (ms) |
AT+UARTTL |
=<bytes> |
AT+UARTTL=<10-1002> |
UART frame length trigger |
| Command | Format | Notes |
|---|---|---|
AT+SSEND |
AT+SSEND=<n> then n bytes |
Send n bytes via SOCKA |
AT+SSENDB |
AT+SSENDB=<n> then n bytes |
Send n bytes via SOCKB |
AT+SRECV |
+ok=<size>,<data> |
Receive from SOCKA |
AT+SRECVB |
+ok=<size>,<data> |
Receive from SOCKB |
| Command | GET Response | SET Format | Notes |
|---|---|---|---|
AT+VER |
=3.00.16 (2025-02-06 17:00 8M) |
GET-only | HiFlying module version |
AT+UART |
=baud,bits,stop,parity,flow |
AT+UART=baud,bits,stop,parity,flow |
UART parameters |
AT+WANN |
=DHCP,ip,mask,gw |
AT+WANN=DHCP or =STATIC,ip,mask,gw |
STA IP config |
AT+WMODE |
=AP/=STA/=APSTA |
AT+WMODE=AP/STA/APSTA |
WiFi mode (triggers restart) |
AT+WSKEY |
=OPEN,NONE or =WPA2PSK,AES,<pw> |
AT+WSKEY=WPA2PSK,AES,<password> |
WiFi security (8→64 chars) |
No password required unless noted. Matched before table lookup.
| Command | GET Response | SET Format | Notes |
|---|---|---|---|
AT+WEBU |
=admin,admin (web credentials) |
AT+WEBU=<user>,<pass> |
→ confirmed → returns web UI credentials |
AT+Z |
→ | AT+Z |
Device reboot (immediate) |
AT+RELD |
+ok=rebooting... |
AT+RELD |
Device reboot with message |
AT+WALK |
WiFi AP scan list | GET-only | Scans and returns nearby APs |
AT+WSCAN |
WiFi scan (short) | AT+WSCAN=<ssid> |
Site survey |
AT+WSCANS |
WiFi scan (extended) | AT+WSCANS=<ssid> |
Extended site survey |
AT+WSSSID |
current SSID | AT+WSSSID=<ssid> |
Set target AP SSID |
AT+BLEADV |
→ | AT+BLEADV=<payload>,<encoding> |
Set BLE advertisement payload |
AT+BLECONFIG |
→ | AT+BLECONFIG=<mode>,<ssid> |
BLE provisioning mode |
AT+BLENTFUUIDW |
→ | AT+BLENTFUUIDW=<uuid> |
Set BLE notify characteristic UUID |
AT+BLEINDUUIDW |
→ | AT+BLEINDUUIDW=<uuid> |
Set BLE indicate characteristic UUID |
AT+BLEADP |
→ | AT+BLEADP=<params> |
BLE advertising interval |
AT+CFGTF |
→ | AT+CFGTF |
Factory config transfer (internal) |
AT+YUMV |
→ | AT+YUMV=on/off |
YZ upgrade mode enable/disable |
AT+UPURL |
→ | AT+UPURL=<url> |
Firmware OTA URL → triggers upgrade |
AT+INVUPURL |
→ | AT+INVUPURL=<url> |
Inverter OTA URL |
AT+WEBUPURL |
→ | AT+WEBUPURL=<url> |
Web page OTA URL |
AT+SUPDATE |
→ | AT+SUPDATE=<url> |
Software update trigger |
AT+UPGRADE |
→ | AT+UPGRADE=<url> |
Upgrade trigger |
AT+YWFPW |
→ | AT+YWFPW=<password> |
WiFi password change |
AT+WADHCP |
→ | AT+WADHCP |
Trigger DHCP assignment |
Authentication notes:
- Single password → all SET commands that require authentication check against
RAM[0x42020960], default214028. There is no second password. - Some handlers call helper
sub_230A00DE(arg)which doesMEMORY[0x42020960] == sub_230C6026(arg)internally; others (YZRSTTIME, YZDEVCTIME, ADDRESS, YZACK, etc.) do the same comparison inline → behavior is identical. - none = no password check at all
Error codes: -1 wrong password or validation failure; -2 wrong arg count (handler-specific); -3 wrong arg count (handler-specific); -4 arg count wrong or value out of range
All SET commands that modify yz_flash config block call sub_230A0BA6() to persist to flash at 0x400000 + 0x480000 (588 bytes). Exceptions noted.
| Command | Handler | GET Response | Notes |
|---|---|---|---|
AT+VER |
sub_2309EB06 |
+ok=HF3.00.16 (2025-02-06 17:00 8M) |
WiFi module version, hardcoded |
AT+YZVER |
sub_2309EA14 |
+ok=MW4_01_5429_SS_04_00.00.00.0B |
Firmware version, hardcoded |
AT+YZBVER |
sub_2309E932 |
+ok=BV<hex8> |
Bootloader version derived from RAM[0x42020B30] |
AT+DTYPE |
sub_2309EA62 |
+ok=<type>,<secondary> |
Device type from RAM[0x42020954], RAM[0x420214DC] |
AT+OFFLINENUM |
sub_2309EAB8 |
+ok=<count> |
Offline record counter at RAM[0x42020CB0] (signed long) |
AT+YZSIZE |
sub_2309EB64 |
+ok=8M FLASH |
Flash size string; RAM[0x42020AE7]: 2→"2M", 4→"4M", 8→"8M" |
AT+YZFACTEST |
sub_2309EBFA |
+ok=sucess,<pct> or +ok=fail,<pct> |
Factory test result; note firmware typo "sucess" (sic) |
AT+GLTYPE |
sub_2309E92E |
+ok |
Stub function, always returns 0 |
AT+YZFLSHTEST |
sub_2309F694 |
(no args, no response) | Writes 0→63 pattern to flash 0x604000, reads back, verifies; returns 0=pass, -3=fail, -1=no flash |
| Command | Handler | GET Response | SET Format | What SET writes |
|---|---|---|---|---|
AT+YZNETCHECK |
sub_230A1598 |
+ok=<val> |
AT+YZNETCHECK=<val> |
RAM[0x42020B18] |
AT+YZPCHECK |
sub_230A1604 |
+ok=<val> |
AT+YZPCHECK=<val> |
RAM[0x42020B19] |
AT+ADTIME |
sub_2309F16E |
+ok=<timestamp>,<mode> |
AT+ADTIME=<ts>,<mode> |
RAM[0x42021F18] = ts → RAM[0x42021F10]; triggers time update; fails if ts → current |
AT+IVGET |
sub_2309F75E |
+ok=<val> |
AT+IVGET=<val> |
RAM[0x420214E0] |
AT+YZFMODE |
sub_230A47CC |
+ok=<v0>,<v1>,<v2> |
AT+YZFMODE=<v0>,<v1>,<v2> |
RAM[0x42021570/74/78]; flash at 0x58F000+0x590000 (NOT yz_flash) |
AT+SSLSER |
sub_230A17DC |
+ok=<host>,<client_id>,<port> |
AT+SSLSER=<host>,<client_id>,<port> |
host→RAM[0x42020A0F] (max 50 B), client_id→RAM[0x420209FF] (max 16 B), port→RAM[0x42020A44] (1→65535) |
AT+MQTTSER |
sub_230A17DC |
same as SSLSER | same as SSLSER | Identical handler to SSLSER |
Security note: AT+SSLSER / AT+MQTTSER require no password → any client on port 48899 can redirect the device's cloud connection to an arbitrary server.
| Command | Handler | SET Format | Effect |
|---|---|---|---|
AT+HTTPPOST |
sub_230A0510 |
AT+HTTPPOST=<url>,<data> |
Performs HTTP POST to <url> with <data>; returns +ok=<response> |
AT+WEBUPURL |
sub_230A31A2 |
AT+WEBUPURL=<url> |
Stores OTA URL at RAM[0x42020D30] (256 bytes cleared first); triggers OTA |
AT+HTTPPOSTFILE |
sub_230A46B6 |
AT+HTTPPOSTFILE=<url>,<size> |
POSTs flash content from 0x604000 in 1024-byte chunks to <url> |
AT+YZLOG |
sub_2309F2AE |
AT+YZLOG=<op>,<sn>,<count> |
op=0: dump inverter registers; op=1: log query (sn must be >0); op=2→9: extended log ops |
AT+INVDATA |
sub_2309F3DA |
AT+INVDATA=<reg_byte>,<inv_addr> |
Reads Modbus register from inverter; returns +ok=<hex bytes> or +ok=no data |
AT+SDTT |
sub_2309F4FA |
AT+SDTT=<hex_bytes> |
Sends raw Modbus query from hex string; returns +ok=<result> or +ok=no data |
AT+FILEGET |
sub_2309EEB2 |
AT+FILEGET=<proto>,<dtype>,<ftype>,<comm>,<gw_sn>,<inv_sn>,<fname>,<fpath> |
≥8 args; triggers background file download; sets start flag at RAM[0x420223E4]; returns +ok=http,post |
| Command | Handler | GET Response | SET Format | What SET writes |
|---|---|---|---|---|
AT+YZCATTIME → |
sub_230A13B8 |
+ok=<byte> |
AT+YZCATTIME=214028,<N> |
Upload period byte at RAM[0x42020B12] |
AT+YZEXPFUN |
sub_230A1436 |
+ok=<dword> |
AT+YZEXPFUN=214028,<N> |
RAM[0x42020B14] (default=3; purpose TBD) |
AT+TIME |
sub_230A0BF6 |
+ok=<v1>,<v2>,<v3> |
AT+TIME=214028,<v1>,<v2>,<v3> |
RAM[0x4202094C] (1→60), RAM[0x4202094E] (1→9215), RAM[0x42020950] (1→9215) |
AT+KEY / AT+YZTIME |
sub_230A0CC4 |
+ok=<val> |
AT+KEY=214028,<N> |
Writes new password to RAM[0x42020960]; N → 999999 |
AT+MTYPE |
sub_230A0D50 |
+ok=<val> |
AT+MTYPE=214028,<val> |
Meter type RAM[0x42020954]; val < 65536 |
AT+CNMOSAVE |
sub_230A0DD6 |
+ok=<idx>,<b1>,<b2>,<name>,<addr>,<port> ≥4 |
AT+CNMOSAVE=214028,<idx>,<b1>,<b2>,<name>,<addr>,<port> |
76-byte config struct at 0x42020964 + 76*idx; idx 0→3; name → 16 B; addr → 49 B; port 1→65535 |
AT+DEVSELCTL |
sub_230A0F58 |
+ok=on / +ok=off |
AT+DEVSELCTL=214028,on or off |
RAM[0x42020A98] = 1 or 0 |
AT+DEVICENUM |
sub_230A1016 |
+ok=<count> |
AT+DEVICENUM=214028,<N> |
Connected device count RAM[0x4202094D]; N → 2 |
AT+NDBGS |
sub_230A109A |
+ok=<0|1> |
AT+NDBGS=214028,<0|1> |
Debug flag RAM[0x42020A99]; val must be 0 or 1 |
AT+FFCLEAR |
sub_230A1120 |
+ok=<val> |
AT+FFCLEAR=214028,1 |
Erases flash loop pages at [0x4200F43C..0x4200F450] step 4096; val must be exactly 1 |
AT+LANGSEL |
sub_230A11C0 |
+ok=<lang> |
AT+LANGSEL=214028,<str> |
9-char language/locale string at RAM[0x42020A9A] |
AT+YZPLAN |
sub_230A11C0 |
+ok=<str> |
AT+YZPLAN=214028,<str> |
Same handler as LANGSEL; same effect |
AT+APSOCKET |
sub_230A1234 |
+ok=<val> |
AT+APSOCKET=214028,<val> |
RAM[0x42020AE4] |
AT+YZWMODE |
sub_230A12B2 |
+ok=<mode> |
AT+YZWMODE=214028,<mode> |
WiFi mode RAM[0x42020B10] |
AT+YZWAKEYCTL |
sub_230A1330 |
+ok=<val> |
AT+YZWAKEYCTL=214028,<val> |
RAM[0x42020B11]; also triggers BLE name update (sub_230A0570) |
AT+YZFTIME |
sub_230A1670 |
+ok=<val>,<str> |
AT+YZFTIME=214028,<val>,<str> |
RAM[0x42020AA4] (uint32) + string at RAM[0x42020AA8] |
AT+YZSPEEDREAD |
sub_230A1A48 |
+ok=<val> |
AT+YZSPEEDREAD=214028,<val> |
RAM[0x42020B48] |
AT+YZSYSVER |
sub_230A1D5A |
+ok=<val> |
AT+YZSYSVER=214028,<val> |
System version RAM[0x42020B30] |
AT+YZSYSCON |
sub_230A1DCE |
+ok=<val> |
AT+YZSYSCON=214028,<val> |
RAM[0x42020B20] |
AT+YZDEFC |
sub_230A1E42 |
+ok=<val> |
AT+YZDEFC=214028,<val> |
Default config flag RAM[0x42020B2E] |
AT+WEBCTL |
sub_230A1F1C |
+ok=<val> |
AT+WEBCTL=214028,<val> |
Web control flag RAM[0x42020B4F] |
AT+SKA |
sub_230A1F9A |
+ok=<val> |
AT+SKA=214028,<val> |
RAM[0x42020B50] |
AT+YZFNL |
sub_230A1528 |
+ok=<v1>,<v2> |
AT+YZFNL=214028,<v1>,<v2> |
RAM[0x42020B44] (uint16) + RAM[0x42020B46] (uint16) |
AT+TS_SN |
sub_230A14B4 |
+ok=<sn> |
AT+TS_SN=214028,<sn> |
16-char SN string at RAM[0x42020B34] |
AT+YZSYSKEY |
sub_230A0378 |
+ok=<hex16> |
AT+YZSYSKEY=214028,<key> |
16-byte system key via sub_23019440/sub_2301939C |
Important: AT+KEY / AT+YZTIME (handler sub_230A0CC4) writes the new password value directly to RAM[0x42020960]. This command changes the password itself. Requires the current password as first argument.
| Command | Handler | GET Response | SET Format | What SET writes |
|---|---|---|---|---|
AT+ADDRESS |
sub_230A0686 |
+ok=<v1>,<v2> |
AT+ADDRESS=214028,<v1>,<v2> |
RAM[0x42020958] + RAM[0x4202095C]; triggers BLE/AP name update and flash erase 0x504000 |
AT+YZACK |
sub_230A1704 |
+ok=<en>,<v0>,<v1> |
AT+YZACK=214028,<en>,<v0>,<v1> |
en (0/1) → bit3 of RAM[0x420209FE]; v0 (0/1) → RAM[0x42020AD0]; v1 (0→10) → RAM[0x42020AD1] |
AT+YZDBNUM |
sub_230A18F6 |
+ok=<v1>,<v2>,<v3> |
AT+YZDBNUM=214028,<v1>,<v2>,<v3> |
RAM[0x42020AD2], RAM[0x42020AD3], RAM[0x42020AD4]; note: arg[2] written to both AD3 and AD4 (bug) |
AT+YZDB |
sub_230A1AC0 |
+ok=<b0>,<b1>,<b2>,<b3> |
AT+YZDB=214028,<b0>,<b1>,<b2>,<b3> |
Bits 0→3 of RAM[0x42020AD5]; also arg[4]→RAM[0x42020AD2], arg[2]→RAM[0x42020AD3/D4] |
AT+APSTAT |
sub_230A1C22 |
+ok=<v1>,<v2> |
AT+APSTAT=214028,<v1>,<v2> |
RAM[0x42020AE5] + RAM[0x42020AE6] |
AT+YZRSTTIME |
sub_230A1CBA |
+ok=<ADC>,<AE0> |
AT+YZRSTTIME=214028,<0|1>,<N> |
N1 (0 or 1) → RAM[0x42020ADC]; N2 → RAM[0x42020AE0] (3-hour reset period) |
AT+YZDEVCTIME |
sub_230A199E |
+ok=<B4A>,<B4C> |
AT+YZDEVCTIME=214028,<v1>,<v2> |
v1 (1→9215) → RAM[0x42020B4A]; v2 (1→9215) → RAM[0x42020B4C] (default=60) |
AT+SERCH |
sub_230A1EB6 |
+ok=<val> |
AT+SERCH=214028,<val> |
RAM[0x42020B1B] (signed) |
AT+WKTIME |
sub_2309F214 |
+ok=<base_ts>,<secondary> |
AT+WKTIME=214028,<ts> |
RAM[0x42021F10] (wake time base); triggers NTP update |
AT+YZAPP |
sub_2309EE46 |
(no GET) | AT+YZAPP=214028,<net|inv> |
Sets app mode flag at RAM[0x4200F4E1] = 1 if mode valid, 0 otherwise |
AT+ERRCLR |
sub_2309ECF2 |
(no GET) | AT+ERRCLR=214028,<mode>,<sub> |
mode=0xFF: erase all data flash; mode=0xF0,sub=0→10: selective region erase |
| Command | Handler | Format | Notes |
|---|---|---|---|
AT+YZFLASHREAD |
sub_2309EC7E |
GET: AT+YZFLASHREAD → +ok=<counter>; SET: AT+YZFLASHREAD=<hex_addr>,<len> → +ok=<b0>,<b1>,... |
No password; reads raw flash bytes at any address; e.g. AT+YZFLASHREAD=400014,8 reads password1 |
AT+YZEARSE |
sub_230A0292 |
AT+YZEARSE=214028,<mode>[,<sub>] |
mode=0xFF: erase data flash + loop pages; mode=0xF0,sub=0: erase 0x504000+0x505000; mode=0xF0,sub=1: erase loop pages; no GET |
AT+IFILE |
sub_230A3BF0 |
AT+IFILE=R,<idx> or AT+IFILE=W,<idx>,<val> |
Read/write internal file fields by index 0→80; no password; field map in handler; index 18 reads/writes 8-byte structs |
Single handler (sub_230A13B8). Confirmed from decompiled C:
// GET (a2==0): returns MEMORY[0x42020B12] as %u
// SET (a2==2): password + value
if ( a2 == 2 ) {
if ( sub_230A00DE(*a3) ) // validate password against MEMORY[0x42020960]
MEMORY[0x42020B12] = sub_230C6026(a3[1]);
sub_230A0BA6(); // flash save → both flash slots
} else if ( a2 != 0 ) return -4;GET: AT+YZCATTIME
Reply: +ok=<N> (current upload period byte, default = 1)
SET: AT+YZCATTIME=214028,<N>
Reply: +ok (value written and persisted to flash)
→ Confirmed working on device 192.168.88.38 with password 214028.
Single handler (sub_230A1436). Confirmed from decompiled C:
// GET (a2==0): returns MEMORY[0x42020B14] as %u
// SET (a2==2): password + value
if ( a2 == 2 ) {
if ( sub_230A00DE(*a3) ) // validate password
MEMORY[0x42020B14] = sub_230C6026(a3[1]);
sub_230A0BA6(); // flash save
} else if ( a2 != 0 ) return -4; // wrong arg count → +ERR=-4GET: AT+YZEXPFUN
Reply: +ok=<MEMORY[0x42020B14]>
SET: AT+YZEXPFUN=214028,<value>
Effect: MEMORY[0x42020B14] = value; flash saved
Note: 0x42020B14 is a different register from 0x42020B12 (upload period byte). Purpose of 0x42020B14 is unknown (default = 3 from init code). AT+YZEXPFUN is NOT the upload period → use AT+YZCATTIME for that.
GET and SET. Reads/writes RAM[0x42020ADC] (watchdog countdown, byte) and RAM[0x42020AE0] (3-hour reset period, dword).
GET: AT+YZRSTTIME
Reply: +ok=<ADC_val>,<AE0_val>
SET: AT+YZRSTTIME=214028,<N1>,<N2>
N1 must be 0 or 1 → stored to RAM[0x42020ADC]
N2 → stored to RAM[0x42020AE0]
Flash saved via sub_230A0BA6
Password = same RAM[0x42020960] (default 214028), compared inline: sub_230C6026(*a3) == MEMORY[0x42020960]
GET and SET. Operates on RAM[0x42020B4A] (uint16) and RAM[0x42020B4C] (uint16, default=60).
GET: AT+YZDEVCTIME
Reply: +ok=<B4A>,<B4C>
SET: AT+YZDEVCTIME=214028,<val1>,<val2>
val1: 1→9215 → stored to RAM[0x42020B4A]
val2: 1→9215 → stored to RAM[0x42020B4C]
Flash saved via sub_230A0BA6
Password = same RAM[0x42020960] (default 214028), compared inline.
The default val2 = 60 almost certainly means 60 seconds → a 1-minute polling period or similar timing unit. This is the most likely parameter the manufacturer changes remotely to alter upload frequency.
The manufacturer's cloud backend sends AT+ commands directly to the device via the TLS socket (YZSSL task → sub_2309FA8A). The commands are plain ASCII, prefixed AT+.
Working commands via deyeat.exe (TCP port 48899, confirmed working):
First, read current values:
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZCATTIME" # +ok=1
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZDEVCTIME" # +ok=65535,65535
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZRSTTIME" # +ok=0,3
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZEXPFUN" # +ok=<word at 0x42020B14>Then read password from flash (single password, all commands):
# YZFLASHREAD format: first arg ignored, second arg = flash offset in decimal
# 0x400014 = 4194324
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZFLASHREAD=0,4194324"
# bytes[0..3] little-endian uint32 = current password value→ CONFIRMED WORKING → AT+YZCATTIME=214028,<N>
Password 214028 is the default and has NOT been overwritten on this device. Commands verified on device at 192.168.88.38:
# Set upload period to 5
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZCATTIME=214028,5"
# → +ok
# Verify
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZCATTIME"
# → +ok=5
# Restore to default (1 = 5-minute upload cycle)
.\deyeat.exe -t 192.168.88.38:48899 -xat "AT+YZCATTIME=214028,1"
# → +okKnown values:
1= default (5-minute cycle → the baseTime300S_Event_Disposetimer is always 5 min; this byte is a multiplier)- Higher values = less frequent uploads (e.g.
5= every 5×5 = 25 min, or possibly just interpreted differently by cloud) - The exact unit/multiplier semantics need field-testing to confirm
Note on AT+YZEXPFUN=05 → +ERR=-4: error code -4 = wrong number of arguments. Requires password,value format (2 args), not just value.
There is only one AT command password. All authenticated SET commands compare arg[0] against RAM[0x42020960].
| RAM Address | Flash Address | Default Value | Note |
|---|---|---|---|
0x42020960 |
0x400014 |
214028 |
In yz_flash config block (588 bytes at 0x400000) |
- Default
214028is hardcoded in init:MEMORY[0x42020960] = 214028(sub_2309FDE8) - To change:
AT+KEY=214028,<new_password>writes toRAM[0x42020960]and flashes - The web UI password (
admin/admin, stored in HiFlying NVRAM asCFG_PWPWD) is completely independent → different storage, different mechanism, NOT used for AT commands
Handler sub_2309EC7E, line 145584:
v6 = sub_230C8208(*(unsigned __int8 **)(a3 + 4), &v7, 10); // reads arg[1] (SECOND arg) as decimal
sub_230A92EA(v6, (int)v8, 8); // hardcoded 8 bytes, length arg ignoredActual format:
AT+YZFLASHREAD=<ignored>, <decimal_flash_offset>
→ first arg → second arg = actual flash byte offset (decimal)
- First argument is completely ignored by the handler
- Second argument = raw flash byte offset, base-10 decimal
- Length is always 8 bytes (hardcoded; the "length" you pass is ignored)
All previous tests (AT+YZFLASHREAD=400014,8, AT+YZFLASHREAD=4194324,8, AT+YZFLASHREAD=0x400022,8) all had 8 as the second arg → all read flash offset 8 → BL602 firmware image FCFG header (FCFG magic = bytes 70,67,70,71 = 0x46,0x43,0x46,0x47). Always identical.
Correct commands:
| Read target | Flash address | Correct command |
|---|---|---|
| yz_flash start | 0x400000 = 4194304 |
AT+YZFLASHREAD=0,4194304 |
AT password (RAM[0x42020960]) |
0x400014 = 4194324 |
AT+YZFLASHREAD=0,4194324 |
Upload period byte (RAM[0x42020B12]) |
0x4000C6 = 4194502 |
AT+YZFLASHREAD=0,4194502 |
| yz_flash backup | 0x480000 = 4718592 |
AT+YZFLASHREAD=0,4718592 |
Response = 8 decimal byte values. Password = bytes[0..3] as little-endian uint32.
→ Confirmed on live device (192.168.88.38):
AT+KEY → +ok=214028 (reads RAM[0x42020960] directly)
AT+YZFLASHREAD=0,4194324 → +ok=12,68,3,0,0,2,20,52
bytes[0..3] LE: 12 + 68×256 + 3×65536 + 0 = 214028 → matches RAM
RAM value = flash value → password has NOT been overwritten after SolarMan registration on this device.
CFG_SDC (index 15 in the off_230F31A8 key table) is not a standalone HTTP endpoint. It is embedded by the template engine into the HTML page at load time.
Correct method:
GET http://10.10.100.100/index_cn.html- Parse
var CFG_SDC = "...";from the JavaScript block
Python example:
import requests, re
r = requests.get("http://10.10.100.100/index_cn.html")
m = re.search(r'var CFG_SDC = "([^"]+)"', r.text)
if m:
parts = m.group(1).split(",") # format: "device_type,da_id,protocol_ver"
da_id = int(parts[1]) # Modbus slave address (= 162 = 0xA2)CFG_SDC format: "<device_type>,<da_id>,<protocol_ver>" where da_id is the Modbus slave address used internally (confirmed 162).
The Modbus RS-485 slave address of the EV charger is NOT exposed via any AT command.
AT+ADDRESS GET returns RAM[0x42020958] and RAM[0x4202095C] → both contain the device SN (3422438570). This is the cloud device identifier, not the RS-485 slave address.
AT+DEVICENUM GET returns RAM[0x4202094D] → the number of connected inverter slots (0→2). Although CFG_SDC writes da_id to this same address (sub_230A64D4, line 151851), AT+DEVICENUM SET is clamped to → 2, and AT+DEVICENUM on port 48899 returns the UDP discovery response instead of the AT response on this device.
The RS-485 slave address 162 (0xA2) is hardcoded as a literal in sub_230B29BC → the Modbus write function called from CFG_CHARGE_POWER. It is not stored in the yz_flash config block and is not readable or writable via any AT command.
| Method | Command / Path | Result |
|---|---|---|
| Web UI (most reliable) | GET http://10.10.100.100/index_cn.html → parse var CFG_SDC = "..." → field[1] |
da_id value |
| From firmware | Hardcoded in sub_230B29BC |
162 (0xA2) |
| AT commands | ??? Not available | → |
Cloud does not query slave addresses. The module itself PUSH-es inverter data via the TLS socket (YZSSL task). The cloud hierarchy is:
- Gateway = the Wi-Fi logger module, identified by
GWSN=<SN>(e.g.3422438570) - Device = the EV charger, identified by
yz_flash.da_id(RAM[0x42020958]) which the module reads from the charger via Modbus at registration time and stores in yz_flash
The RS-485 slave address 162 is purely a transport-layer bus address, invisible to the cloud. The cloud device ID (da_id) is a separate application-layer identifier written into RAM[0x42020958] → readable via AT+ADDRESS GET (first value in the pair).
The device responds to UDP broadcast on port 48899:
| Message | Response | Fields |
|---|---|---|
WIFIKIT-214028-READ |
<IP>,<MAC>,<SN> |
IP address, MAC, serial number |
HF-A11ASSISTHREAD |
<IP>,<MAC>,<DeviceName> |
IP address, MAC, device name |
Python code:
import socket, time
DISCOVERY_PORT = 48899
DISCOVERY_MESSAGES = [b"WIFIKIT-214028-READ", b"HF-A11ASSISTHREAD"]
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.settimeout(0.5)
for msg in DISCOVERY_MESSAGES:
sock.sendto(msg, ("255.255.255.255", DISCOVERY_PORT))
try:
data, addr = sock.recvfrom(256)
print(f"Response to {msg}: {data.decode()} from {addr[0]}")
except socket.timeout:
pass
sock.close()The Time300S_Event_Dispose function drives the cloud upload cycle. States:
| State | Name | Action |
|---|---|---|
| 0 | sInverter300msEventStart |
Start 5-min timer |
| 1 | sComplexWifiTCPLKBCmd |
Check WiFi/TCP connection |
| 2 | sComplexWifiIPSTCPCmd |
Send IP/TCP command |
| 3 | sComplexInverterMemoryWriteCmd |
Write inverter memory |
| 4 | sComplexServerACKTimeOut |
Wait for server ACK |
| 5 | sComplexWifiEndDataCmd |
End data sequence |
| 6 | sComplexServerCmdTimeOut |
Server command timeout |
| 7 | sComplexInverterErrorCmd |
Handle inverter error |
Key log: "Time300S_Event_Dispose##cyg_12hors_delays=%0x,yz_flash.yz_timed_start.enable=%0x" → the yz_timed_start.enable field in the yz_flash block at 0x42021570 controls whether the scheduled upload is active.
| Parameter | Location | Default | Configurable? | How |
|---|---|---|---|---|
| 5-min upload cycle timer | Time300S_Event_Dispose state machine |
300 s | No | Hardcoded in RTOS timer |
| Upload period byte | RAM[0x42020B12] |
1 | Yes | AT+YZCATTIME=214028,<N> → confirmed |
| Device timing params | RAM[0x42020B4A], 0x42020B4C |
?, 60 | Yes | AT+YZDEVCTIME=<pwd>,X,Y |
| 3-hour reset period | RAM[0x42020AE0] |
3 | Yes | AT+YZRSTTIME=<pwd>,N1,N2 |
| Schedule enable flag | yz_timed_start.enable in yz_flash |
? | Possibly | Via yz_flash flash block write |