Donec Facilisis Lacinia

You are right about WD’s ownership.

I did the same once when I was experimenting, but reverted it to the original.


Docker used to be WDs tool of choice for installing custom apps onto these NAS units. Then they came up with the new Framework which they released and is now driven from the “Apps” tab in the Dashboard. I personally have docker used on my NAS and run SoftEther VPN in a docker container. Definitely great to have it on there although the docker version installed is getting a bit dated now.



Check out My Cloud Developer

Here’s an strace (installed with entware) of fan_control in debug mode:

It’s a loop with a countdown of 60 seconds and then prints temperature and fan info.
Might contain something of interest…

EDIT with more info:

fan_control 0 d

[fan_control.c:424] standby_flag=0
[fan_control.c:511] HD1 temperature 30
[fan_control.c:511] HD2 temperature 31
[fan_control.c:511] HD3 temperature 30
[fan_control.c:511] HD4 temperature 31
[fan_control.c:1939] current board temperature is 30
[fan_control.c:1940] current hdd temperature is 31
[fan_control.c:656] temperature=31
[fan_control.c:1438] current fan_rpm=510
[fan_control.c:776] uP cmd:up_send_ctl temperature 31 87
[fan_control.c:887] calculate temperature=31
[fan_control.c:890] WD daemon send alert
[fan_control.c:268] sleep duration is 60

Dig deeper to get any info on fan_rpm

strace fan_control -g 4
connect(6, {sa_family=AF_UNIX, sun_path="/var/run/wdtms.sock"}, 21) = 0
sendmsg(6, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="<?xml version=\"1.0\" encoding=\"ut"..., iov_len=202}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 202
recvmsg(6, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="<?xml version=\"1.0\" encoding=\"ut"..., iov_len=2048}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 253

This shows communication with the wdtms socket.

strace -e read=6 -e write=6 fan_control -g 4
    <?xml version="1.0" encoding="utf-8"?>.
    <?xml version="1.0" encoding="utf-8"?>.

Sure, have a look at the source code.

The script contains all you need.
When you have entware, just use

opkg install strace

A lot of info comes from running wdtms like this:

export LD_LIBRARY_PATH=/opt/wd/lib:/opt/wd/lib/boost
pkill wdtms
strace -f /opt/bin/wdtms -config=/etc/wd/BNFA-thermal.xml

I’m still going through, but you see commands passing by such as

i2cget -y 8 0x4c 0x10

This is polling the fractional byte of the temperature sensor… seems a bit useless to me but whatever.
More interesting is address 0x00 for the internal sensor and address 0x01 / 0x23 for the external sensors…

Digging deeper into the wdtms.

<?xml version="1.0" encoding="utf-8"?>.

And the response

<?xml version="1.0" encoding="utf-8"?>.

And using strace on wdhws gives a lot more info.

Fan control probably works over the serial bus at /dev/ttyS2, with params B9600 -opost -isig -icanon -echo.
Set the fan speed to 30 RPM as follows

{iov_base="FAN=1E\r", iov_len=7}

/opt/bin/opkg if you don’t update your path :slight_smile:

In the I copy some handy aliases to /etc/profile and I set the path as follows.

export PATH=/opt/bin:/opt/sbin:$PATH

I didn’t apply this to the interactive shell only yet… it’s on my TODO list :slight_smile:

I got a lot of info with this command:

strace -f -e read=7 -e write=7 -e read=3 -e write=3 -e write 15 -o wdhws_out /opt/wd/bin/wdhws -config=/etc/wd/sprite-wdhw.xml

Use the -f flag to catch all the forks, -o to write to a file and -e to capture all file access for the corresponding pointers.
I’ll now try to make a python replacement for these services that can be used on plain debian.

With strace, you can save the output to a file.
You’ll see that sockets are opened from wdtms to the wdhws.socket. This uses a file pointer (a number).
To get all data on that socket, capture the writes and the reads on that number.
Then I cleaned up the hex data to get the xml text.

A sample of the output when you listen to file pointer 3

first it reads a lot of library headers, until
open("/var/run/", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
4147  recvmsg(3,  <unfinished ...>                                                                                                                
4151  <... futex resumed> )             = 0                                                                                                       
4147  <... recvmsg resumed> {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="<?xml version=\"1.0\" encoding=\"ut"..., iov_len=2048}], msg_iovlen
 * 217 bytes in buffer 0                                                                                                                          
 | 00000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  <?xml version="1 |                                                                    
 | 00010  2e 30 22 20 65 6e 63 6f  64 69 6e 67 3d 22 75 74  .0" encoding="ut |                                                                    
 | 00020  66 2d 38 22 3f 3e 0a 3c  64 61 74 61 5f 6d 65 73  f-8"?>.<data_mes |                                                                    
 | 00030  73 61 67 65 3e 3c 69 6e  74 65 72 6e 61 6c 5f 69  sage><internal_i |                                                                    
 | 00040  6e 66 6f 3e 3c 76 65 72  73 69 6f 6e 5f 69 6e 74  nfo><version_int |                                                                    
 | 00050  3e 31 3c 2f 76 65 72 73  69 6f 6e 5f 69 6e 74 3e  >1</version_int> |                                                                    
 | 00060  3c 74 79 70 65 5f 73 74  72 69 6e 67 3e 48 57 47  <type_string>HWG |                                                                    
 | 00070  65 74 53 74 61 74 75 73  3c 2f 74 79 70 65 5f 73  etStatus</type_s |                                                                    
 | 00080  74 72 69 6e 67 3e 3c 2f  69 6e 74 65 72 6e 61 6c  tring></internal |                                                                    
 | 00090  5f 69 6e 66 6f 3e 3c 64  61 74 61 3e 3c 68 61 6e  _info><data><han |                                                                    
 | 000a0  64 6c 65 5f 73 74 72 69  6e 67 3e 30 78 62 31 38  dle_string>0xb18 |                                                                    
 | 000b0  38 32 30 3c 2f 68 61 6e  64 6c 65 5f 73 74 72 69  820</handle_stri |                                                                    
 | 000c0  6e 67 3e 3c 2f 64 61 74  61 3e 3c 2f 64 61 74 61  ng></data></data |                                                                    
 | 000d0  5f 6d 65 73 73 61 67 65  3e                       _message>        |                                                                    
4151  futex(0x7f8430002620, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>

It’s my first experience with strace as well :slight_smile:
I suggest you start with strace on fan_control as follows

strace fan_control

It shows you can add args

strace fan_control -g 4

Still not enough info… it spawns extra processes and we’re not logging them yet.

strace -f fan_control -g 4

At the end of the output, you see the std output, fan rpm = 510.
A few lines earlier, you see that /tmp/msg_info.txt is opened and it writes some data.
And I see a message to the LCD screen:

msgsnd(0, {2672, "\f\372\4\203\0\006510rpm\373"}, 13, 0)

It helps to log to a file, use -o.
With vi you can jump fast to keywords. Use / and type ‘open’, then press n / N to jump between them.

When you’ve done this rather easy debugging, you can tackle wdtms and wdhws…

Good luck :slight_smile:

Entware is good. I’ve been using it for a while to run some python based apps like Sickrage…

The nice thing about entware is that it’s native and can be really powerful. But the limitations in some packages will cause frustration … Some projects can be really hard to setup, e.g. virtualenvs with python and openssl. Some tools are broken, e.g. the e2fsprogs.
Native debian would be a blessing.

You could try to compile the sources from git…

Fan control has arrived!

kill `pidof wdhws`              # careful with this!
opkg install python python-pip
pip install pyserial ipython==5.2


Now do this

import serial
ser = serial.Serial('/dev/ttyS2', 9600, timeout=1)

# get status
print ser.readline()
STA=6a      >> means all is good

# set fan speed
print ser.readline()

# read fan speed
print ser.readline()
RPM=01a4     >> this is 420 rpm

# increase fan speed x 4
print ser.readline()

# read fan speed
print ser.readline()
RPM=0618     >> this is 1560 rpm

I’ll now look into the config values.

When you kill all wdhws, the fan remains in manual config mode and it will not spin faster when temperature becomes too high. That’s why I added a warning for people that live in the desert or on planet Venus.

I digged through the wdhws traces:
Here is the wdtms trace that triggers some communication on the bus.

Search for tty, readv(7 and writev(7

I used this cool script in my /etc/profile to upload it:

You may see this command

i2cget -y 8 0x4c 0x1

This is the temperature sensor, see the post half-way this topic.
Datasheet for the EMC1403:

wdtms polls the temperature and requests fan status / rpm from the wdhws.

Next step is to branch of lm-sensors and adapt it to this box.

Temperatures are in celcius. See also the fancontrol -g 0 output.
Compare it with polling address 0x00, 0x01 and 0x23.
Address 0x10 is rubbish, I guess it’s a bug. Who cares about the digits behind the comma…

These are fan related commands I see during startup of wdhws.

get config with 'CFG'       --> CFG=02
set config with 'CFG=2'
set DLS with 'DLS=f'     
get version with 'VER'      --> VER=WD BBC v01
get ISR with 'ISR'          --> ISR=00
set IMR with 'IMR=FF'
get status with 'STA'       --> STA=6a
get temperature with 'TMP'  --> TMP=22 ... 34 degrees celcius

Then it sets fan speed and checks RPM + status.

Polling all possible 3 letter fields results in:

TMP=23   ---> clearly temperature

Then suddenly I got this after sending 'UPD'

========= WDPMC Update Menu v1.0 =============
Reset PMC -------------------------------- 0
Write Image To PMC Internal Flash -------- 1
Read Image From PMC Internal Flash ------- 2
Execute The New Program ------------------ 3
Invalid Number ! ==> The number should be either 1, 2 or 3

I guess I bricked something. Be careful not to spam the poor box with unknown commands!