Skip to content

Instantly share code, notes, and snippets.

@stefaang
Last active March 21, 2024 01:31
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save stefaang/0a23a25460f65086cbec0db526e87b03 to your computer and use it in GitHub Desktop.
Save stefaang/0a23a25460f65086cbec0db526e87b03 to your computer and use it in GitHub Desktop.
Hardware control script for PR2100/PR4100
#!/bin/bash
#
# Post-init script for FreeNAS on Western Digital PR2100/PR4100
# wdhws v1.0 by TFL
#
# BSD 3 LICENSE
#
# thanks unix stackexchange question 231975
setup_tty() {
tty=/dev/cuau3
exec 4<$tty 5>$tty
}
setup_i2c() {
# load kernel modules required for the temperature sensor on the RAM modules
kldload -n iicbus smbus smb ichsmb
}
send() {
setup_tty
# send a command to the PMC module and echo the answer
echo -ne "$1\r" >&5
read ans <&4
if [ "$ans" = "ALERT" ]; then
echo -ne ALERT >&2
exit 2
else
# keep this for debugging failing commands
if [ "$ans" = "ERR" ] || [ -z "$ans" ]; then
echo "CMD $1 gives ERR at $2" >&2
send_empty
ans=$(send "$1" $(($2 + 1)))
#exit 1
fi
fi
# only echo the result for retries ($2 not empty)
if [ ! -z $2 ]; then
echo "CMD $1 gives '$ans' at $2" >&2
fi
echo $ans
send_empty
# deconstruct tty file pointers, otherwise this script breaks on sleep
exec 4<&- 5>&-
}
send_empty() {
# send a empty command to clear the output
echo -ne "\r" >&5
read ignore <&4
}
get_ncpu() {
# get the number of CPUs
sysctl -n hw.ncpu
}
get_coretemp() {
# get the CPU temperature and strip of the Celsius
sysctl -n dev.cpu.$1.temperature | cut -d'.' -f1
}
get_disktemp() {
# get the disk $i temperature only if it is spinning
smartctl -n standby -A /dev/ada$1 | grep Temperature_Celsius | awk '{print $NF}'
}
get_ramtemp() {
# get the memory temperature from the I2C sensor
smbmsg -s 0x98 -c 0x0$1 -i 1 -F %d
}
get_pmc() {
# get a value from the PMC
# e.g. TMP returns TMP=25 --> 25
send $1 | cut -d'=' -f2
}
init() {
setup_tty
setup_i2c
echo "get system status and firmware"
send VER
send CFG
send STA
show_welcome
stop_powerled
}
show_welcome() {
# set welcome message
# maximum "xxx xxx xxx xxx "
send "LN1= FreeNAS "
send "LN2= go go go go go "
}
stop_powerled() {
# stop blinking power LED
send PLS=00
send LED=00 # set to 01 to enable it
send BLK=00
}
show_ip() {
send "LN1=Interface re$1"
ip=$(ifconfig re$1 | grep inet | awk '{printf $2}')
send "LN2=$ip"
}
monitor() {
lvl="COOL"
# check RPM (fan may get stuck) and convert hex to dec
fan=$(get_pmc FAN)
rpm=$((0x$(get_pmc RPM)))
echo "Got rpm $rpm"
if [ "$rpm" != ERR ]; then
if [ "$rpm" -lt 400 ]; then
echo "WARNING: low RPM - $rpm - clean dust!"
fi
fi
# check pmc
tmp=$((0x$(get_pmc TMP)))
if [ "$tmp" -gt 64 ]; then
lvl="HOT"
fi
# check disks [adjust this for PR2100!!]
for i in 0 1 2 3 ; do
tmp=$(get_disktemp $i)
echo "disk $i is $tmp"
if [ ! -z $tmp ] && [ "$tmp" -gt 40 ]; then
echo "Disk $i temperature is $tmp"
lvl="HOT"
fi
done
# check cpu
for i in $(seq $(get_ncpu)); do
tmp=$(get_coretemp $((i-1)))
echo "cpu $i is $tmp"
if [ "$tmp" -gt 50 ]; then
echo "CPU $i temperature is $tmp"
lvl="HOT"
fi
done
# check ram
for i in 0 1; do
tmp=$(get_ramtemp $i)
echo "ram temp is $tmp for $i"
if [ "$tmp" -gt 40 ]; then
echo "RAM $i temperature is $tmp"
lvl="HOT"
fi
done
echo "Temperature LVL is $lvl"
if [ "$lvl" == HOT ] ; then
if [ "$fan" != 40 ]; then
send FAN=40
fi
else
if [ "$fan" != 20 ]; then
send FAN=20
fi
fi
}
check_btn_pressed() {
btn=$(get_pmc ISR)
#echo "Btn is .$btn."
case $btn in
20*)
echo "Button up pressed!"
menu=$(( ($menu + 1) % 3 ))
;;
40*)
echo "Button down pressed!"
menu=$(( ($menu + 2) % 3 ))
;;
*)
return
esac
case "$menu" in
0)
show_welcome
;;
1)
show_ip 0
;;
2)
show_ip 1
;;
# if you add menu items here, update mod 3 uses above
esac
}
# initial setup
init
while true; do
# adjust fan speed every 30 seconds
monitor
# check for button presses
for i in $(seq 30); do
sleep 1
check_btn_pressed
done
done
@yuanjs
Copy link

yuanjs commented Aug 21, 2018

I try this script. after a while, it pop up a error:
./hardwarectl.sh: line 126: 2a: value too great for base (error token is "2a")

@ihongyi
Copy link

ihongyi commented Aug 24, 2018

Can this script be used for dsm 6.1.7?

@stefaang
Copy link
Author

I try this script. after a while, it pop up a error:
./hardwarectl.sh: line 126: 2a: value too great for base (error token is "2a")

The value needs hex2decimal conversion.
Sorry for the late reply, I didn't get a notification of the warning.

use for dsm 6.1.7
I think you'll need another tty port, but yes.

@ihongyi
Copy link

ihongyi commented Sep 25, 2018

hello stefaang
Can you elaborate? Or is there a guide? Sorry, I don't have experience in this area.

@tstyopin
Copy link

tstyopin commented Mar 5, 2020

Hi, Stefaang!
Thank you very much for script, it does a great job!
Something changed in smartctl command, and get_disktemp gives something like 24/55 (it is minimum and maximum lifetime temperature)
I changed script line 65 a little and it worked well by now:
smartctl -n standby -A /dev/ada$1 | grep Temperature_Celsius | cut -d'/' -f37 | awk '{print $NF}'
I don't have a bash scripting experience at all, please correct if needed.
edit:corrected by forum post

@Phone-guy
Copy link

use for dsm 6.1.7
I think you'll need another tty port, but yes.

Can anyone explain how to do this? I am running dsm7.1 on a pr4100, and would love to use this script.... please help

@tstyopin
Copy link

tstyopin commented Apr 14, 2022

Can anyone explain how to do this? I am running dsm7.1 on a pr4100, and would love to use this script.... please help

You should find tty, which used by pr4100 screen, and correct script in row 11.
How to find correct tty (or at least limiting the results)
https://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them

@Phone-guy
Copy link

Can anyone explain how to do this? I am running dsm7.1 on a pr4100, and would love to use this script.... please help

You should find tty, which used by pr4100 screen, and correct script in row 11. How to find correct tty (or at least limiting the results) https://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them

root@PR4100:/# ll /sys/class/tty/*/device/driver
lrwxrwxrwx 1 root root 0 Apr 19 03:57 /sys/class/tty/ttyS0/device/driver -> ../../../bus/platform/drivers/serial8250
lrwxrwxrwx 1 root root 0 Apr 19 03:57 /sys/class/tty/ttyS1/device/driver -> ../../../bus/platform/drivers/serial8250
lrwxrwxrwx 1 root root 0 Apr 19 03:57 /sys/class/tty/ttyS2/device/driver -> ../../../bus/pci/drivers/serial
lrwxrwxrwx 1 root root 0 Apr 19 03:57 /sys/class/tty/ttyS3/device/driver -> ../../../bus/pci/drivers/serial

How do I know which one is talking to the screen?

@tobiabocchi
Copy link

Hey stefan! Thank you for your post on the forum and this script!

Running TrueNAS-13.0-U4, I had to change line 65 to
smartctl -n standby -A /dev/ada$1 | grep Temperature_Celsius | awk '{print $NF-1}'
In order to get the correct value for disk temperature

@superdupe
Copy link

Hey Everyone, have any of you moved to Scale from Core? If so, how did you handle hardware as this script appears to no longer work.

@superdupe
Copy link

FYI for anyone looking for the Scale answer, Scale is now Debian Linux instead of FreeBSD. TrueNAS Core is FreeBSD. In any event, I had to change the tty from tty=/dev/cuau3 to tty=/dev/ttyS2. That appears to the correct setting for Linux on the PR4100.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment