Help with CPU Temperature Email Script

Hello All, @huidbui25 , @Joulinar

Hope everyone is in good health and doing well.

This is great tutorial / script and I have been using this on DietPi running on few RPis. I had two old PC Engines APU 1d boxes lying around with 16GB mSATA and 2GB RAM and dual Atom CPU. It also has 3 Gig ethernet ports, one for WAN and 2 for LAN. I first did BIOS upgrade to latest available. Then used DietPi script for Other hardware on it (after installing Debian 11) and successfully converted to DietPi latest version. Could not make the built-in serial / DB9 port working though.

I was able to see my interfaces and did some quick tests. All looked okay. But then found that every few minutes the box will shutdown. Could not find any clues as to why.

Spent many hours and then reverted back to Debian 11. All those issues went away and I also got control of serial port.
Installed UFW and IP tables persistent and I am able to use NAT also from two LAN ports to go to internet via WAN port. Then installed NoMachine with LXDE and that also works great. Of course Firefox gives some grief in terms of how much CPU hog it is. But that is not a concern, as I will hardly use browser on this box.

I then installed OpenVPN for client and successfully made it auto connect on reboot to the AWS / Ubuntu based PiVPN server.

Then I looked for vcgencmd and found that this package is only available on Raspbian OS. I really wanted to use the script written by @huidbui25. But I then installed sensors package and though to slightly modify the script and achieve the results. The output of sensors is in a different format and it shows temperature twice (for two cores).

I replaced, the part,

tmp_akt=$(vcgencmd measure_temp | cut -f 2 -d “=” | cut -f 1 -d “'” | grep -E ‘^-?[0-9]*.?[0-9]+$’)

with

tmp_chk=$(awk ‘/temp1/ {print +$2}’ <(sensors) | head -1 | cut -b 1,2)

If I try to use the grep part from original script, it complains. Essentially what I find is the the output from my script is essentially a string and not an integer that I need.

Further below, under the statements to check for warning and alarm conditions, if I use bc -l, then it complains of syntax error, obviously, if result of tmp_akt was string and if I remove bc (anyway, I was not using decimals after temp and had cut it to two digit number only), and then this error goes away. But then further down below, the if condition was expecting results to be 0 or 1, which it outputs as string like ‘57 -gt 67’ and ‘57 -gt 77’

So I am looking for some help. In the past, I have twice donated to the project. If I can be helped with this script working on PC Engines box running Debian, I will donate another $100 (USD though I am in Canada) to the project.

Ultimately, I will try to get back to DietPi on these two boxes as well.

Running bash -x on the script shows the issue:

root@TestHost:~# bash -x /usr/local/bin/temp_mon

  • tmp_warn=67
  • tmp_alarm=77
  • tmp_unit=C
  • FROM_NAME=‘PCEng Lab’
  • FROM_ADDRESS=PCEng@domain.com
  • TO_NAME=‘PC Engines’
  • TO_ADDRESS=Lab@domain.com
  • MAIL_FILE=/tmp/mail_tmp.txt
    ++ bc -l
    ++ cut -b 1,2
    ++ head -1
    ++ awk ‘/temp1/ {print +$2}’ /dev/fd/63
    +++ sensors
  • tmp_akt=57
  • ‘[’ ‘’ == test ‘]’
  • ‘[’ -z 57 ‘]’
    ++ echo 57 -gt 67
  • status_warn=‘57 -gt 67’
    ++ echo 57 -gt 77
  • status_alarm=‘57 -gt 77’
  • [[ ‘’ == 0 ]]
  • echo ‘From: “PCEng Lab” PCEng@domain.com
  • echo ‘To: “Lab” Lab@domain.com
  • ‘[’ == 1 ‘]’
    /usr/local/bin/temp_mon: line 47: [: ==: unary operator expected
  • ‘[’ == 1 ‘]’
    /usr/local/bin/temp_mon: line 53: [: ==: unary operator expected
  • echo ‘Content-type: text/plain; charset=utf-8’
  • echo ‘Content-Language: en’
  • echo ‘X-Auto-Response-Suppress: OOF’
  • echo ‘’
  • echo ‘CPU temperature is melting hot!’
  • echo ‘Current temp: 57 °C’
  • ‘[’ ‘’ == test ‘]’
  • echo ‘’
  • echo '— ’
  • echo ‘Sent by temp_mon script’
  • echo ‘’
  • cat /tmp/mail_tmp.txt
  • sendmail -f PCEng@domain.com Lab@domain.com
  • logger ‘temp_mon: high CPU temperature: 57 °C, mail sent’
  • rm /tmp/mail_tmp.txt

Thanks so much.

Further, I have tried to force tmp_akt line by putting declare -i in front, so that results are interpreted as integer rather than string, and then it simply computes that value to be zero. I am sure it is something simple, but having no scripting or programming experience, I am overlooking something stupid.

Just for information that with vcgencmd measure_temp, the output is of the format:
temp=34.2°C

And with sensors, it is
acpitz-acpi-0
Adapter: ACPI interface
temp1: +57.5°C (crit = +110.0°C)

k10temp-pci-00c3
Adapter: PCI adapter
temp1: +57.5°C (high = +70.0°C)
(crit = +100.0°C, hyst = +97.0°C)

So I need to parse one of the temp1 line to extract just the temp of two digits like 57 here and it should be formatted as integer and not as string which messes up the subsequent logic of the script.

Thanks

You can use

cpu=$(</sys/class/thermal/thermal_zone0/temp)
echo "$((cpu/1000)) "

to show you two digit temperature.
(/sys/class/thermal/thermal_zone0/temp outputs the temperatur with 5 digits, the last 3 are decimal places.

Or if you want to work with measure_temp, you can format the output to get only 2 digits or whatevery you want.
Edit: Like:

> sudo vcgencmd measure_temp | cut -b 6,7,8,9
> 47.2

The output without cut would be temp=47.2'C but with cut -b 6,7,8,9 we select the bytes 6,7,8 and 9 and remove the rest.

1 Like

vcgencmd is available on Raspberry Pi only :wink:. So yes, the correct (virtual) file in sysfs needs to be found, which is different on every x86 system. E.g. this should be able to find the correct file:

G_OBTAIN_CPU_TEMP
for i in /sys/class/thermal/thermal_zone[0-9]/temp /sys/class/hwmon/hwmon[0-9]/temp[0-9]_input /sys/devices/platform/coretemp.[0-9]/hwmon/hwmon[0-9]/temp[0-9]_input
do
[[ -e $i ]] && echo "$i : $(<$i)"
done

And the output can be formatted with one decimal place with awk/mawk:

echo 32100 | mawk '{printf("%.1f",$1/1000)}'
32.1

Or combined:

mawk '{printf("%.1f",$1/1000)}' /sys/class/thermal/thermal_zone0/temp
43.3
2 Likes

Thank you both Michalng and Jappe for your very helpful advice. I will try this in the evening when I get home and then get back with results.

And I believe being an old SBC box, it will only have a thermal zone 0. So what will be the correct syntax to divide the output of
/sys/class/thermal/thermal_zone0/temp
by 1000, ignore any decimal places and save the output directly into variable (in my case tmp_akt) so that I can simply replace it in my script.

I will need help with correctly formatted one lines to replace this one below please:

tmp_akt=$(awk ‘/temp1/ {print +$2}’ <(sensors) | head -1 | cut -b 1,2) and it should be an integer.

While I don’t have experience on scripting, but I could fully understand the logic and flow of the original script that works great on RPi and I could also read up and use awk, cut commands to slice the output from debian.

@MichaIng and team, you could start a separate (but locked) thread on learning scripting in practical way, and open it to anyone who will donate a minimum of $20 to the project ;). I am hoping I will have my script working and I will do my share of $100 over the weekend.

I am going to use this as a one line replacement and I am sure this will fix the issues.

tmp_akt=$(mawk ‘{printf(“%.0f”,$1/1000)}’ /sys/class/thermal/thermal_zone0/temp)

But it will be another 6 hours or so before I can test it.

Folks, with your prompt help, my script is fully functional now. It is bit late now (close to mid night) but I will document full details in my reply tomorrow morning. For now, I just want to say thank you to @Jappe for quick idea in right direction and then @MichaIng for a complete solution to my needs. And most credits for this script go to our original author @huidbui25.

And as promised, the USD 100 bounty goes into supporting project development work, tomorrow morning.

Finally, folks, I will still like to have my original code line to be fixed to convert the output of sensors command to the required integer format, just as a learning exercise for myself. So if you folks can advise on troubleshooting and fixing the code for that, it will be great. No rush on that part, when you get chance please.

1 Like

Here is final working script that can be used on RPi or on any Linux platform. I have included some notes to aid novices like myself. Please point out anything wrong I may have written by a quick reading of the comments and I will then fix them.

Here goes:

#*****Full Working Script for CPU Temperature Warning and Alerts via Emails*******
#***** Credits go to original author @huidbui25
#****** https://dietpi.com/forum/t/tutorial-check-cpu-temperature-and-send-warning-mail/3845 *****
# Further modified with assistance of @Jappe and @MichaIng to work on any Linux platform and on RPi
# I am using smtp2go account for email relays and it is free or very low cost depending upong your needs.
# You should have DMA (Dragainfly Mail Agent) package installed (apt install dma) which also installs sendmail. 
# See details of DMA install and configuration on link above.
# **** Script > a series of commands to be executed by the interpretaion by certain interpretor, for automating certain repetitive task.
# Anything preceded by # is a comment and not considered to be a command or statement to be executed.
# bash is used as a interpreter for most Linux Scripts. Shebang (#!) or sharp-bang shown in next statement is not a comment line though. 
# path to bash (shell interpreter) is listed below

#! /bin/bash

## VARIABLES to be used in the Script##

# Input Temperature thresholds  here. Use integers only. And Values are in degree Celsius.
# Alarm and Warnings use separate severity mail subject and also mark the emails as important.
# tmp_unit should not be changed to F or K or R:). This variable is used for simple appending for email text.
tmp_warn="65"
tmp_alarm="78"
tmp_unit="C"

# The info needed for sending mails
# To send to multiple recipients, specify multiple, to addresses, by comma separation.
FROM_NAME="PCEng Lab"
FROM_ADDRESS="PCEng@domain.com"
TO_NAME="Lab"
TO_ADDRESS="lab@domain.com"
MAIL_FILE="/tmp/mail_tmp.txt"


## SCRIPT ##

# Statement below stores the current CPU temperature to a variable called tmp_chk
# mawk (a AWK language interpreter, AWK being last name initials of 3 engineer authors of this tool and >
# m in MAWK is first initial of author of interpreter),>
# parses the output of command listed here as first place argument (denoted by $1), > 
# using printf function by diving the command output by 1000 and with zero decimal places (0f)
# variable value assigned or computed is stored into $variable. To use variable subsequently, we then use it as $variable

tmp_chk=$(mawk '{printf("%.0f",$1/1000)}' /sys/class/thermal/thermal_zone0/temp)

# this section below is just for a test run to check emails are fired from the Script without generating conditions for Script to act.

if [ "$1" == "test" ]
# $1 equal to test here implies that when we run the script by appending first place argument as test

then
  tmp_chk="200"

# this is to continue downwards the execution of script

elif [ -z "$tmp_chk" ]

# -z is used to check if value of following variable is empty and then condition output is true, else false

then
  echo "${0##*/}: Error at reading the CPU temperature. Unknown command? No numeric value? Aborting script."
  exit 1
fi
# ${0##*/} prints the script name by removing the path name from the script file. So /usr/local/bin/temp_mon becomes temp_mon.

# Section below determines whether the current temperature is above warning and/or alarm threshold

status_warn=$(echo $tmp_chk '>=' $tmp_warn | bc -l)
status_alarm=$(echo $tmp_chk '>=' $tmp_alarm | bc -l)

# bc is important to allow arithmetic operations on decimal numbers, else bash scripts can only handle integers.

# Section below terminates script when neither of two thresholds condition occur

if [[ "$status_warn" == 0 && "$status_alarm" == 0 ]]
then
  exit 0
fi

# Generates the mail text that will be sent out

echo "From: \"$FROM_NAME\" <$FROM_ADDRESS>" > $MAIL_FILE
echo "To: \"$TO_NAME\" <$TO_ADDRESS>" >> $MAIL_FILE

if [ $status_alarm == 1 ]
then
  echo "Subject: Temperature ALARM" >> $MAIL_FILE
  echo "X-Priority: 1 (Highest)" >> $MAIL_FILE
  echo "X-MSMail-Priority: High" >> $MAIL_FILE
  echo "Importance: High" >> $MAIL_FILE
elif [ $status_warn == 1 ]
then
  echo "Subject: Temperature warning" >> $MAIL_FILE
fi

# -eq or == for comparison, but not = in  the, if, elif (else if) conditions.
 
echo "Content-type: text/plain; charset=utf-8" >> $MAIL_FILE
echo "Content-Language: en" >> $MAIL_FILE
echo "X-Auto-Response-Suppress: OOF" >> $MAIL_FILE
echo "" >> $MAIL_FILE
echo "CPU temperature is High!" >> $MAIL_FILE
echo "Current temp: $tmp_chk °$tmp_unit" >> $MAIL_FILE
if [ "$1" == "test" ]
then
  echo "" >> $MAIL_FILE
  echo "Please note:"  >> $MAIL_FILE
  echo "Script was run in *test mode* to check email delivery, this not the real CPU temperature!" >> $MAIL_FILE
fi
echo "" >> $MAIL_FILE
echo "--- " >> $MAIL_FILE
echo "Sent by ${0##*/} script" >> $MAIL_FILE
echo "" >> $MAIL_FILE

# gathers the text deposited into MAIL_FILE by above actions and sends using sendmail mail relay agent. 
cat $MAIL_FILE | sendmail -f $FROM_ADDRESS $TO_ADDRESS && logger "${0##*/}: high CPU temperature: $tmp_chk °$tmp_unit, mail sent" 

# deletes the temp file
rm $MAIL_FILE

Script Execution Steps

***** When all is good *****

root@TestHost:~# bash -x /usr/local/bin/temp_mon
+ tmp_warn=60
+ tmp_alarm=75
+ tmp_unit=C
+ FROM_NAME='PCEng Lab'
+ FROM_ADDRESS=PCEng@domain.com
+ TO_NAME='Lab'
+ TO_ADDRESS=lab@domain.com
+ MAIL_FILE=/tmp/mail_tmp.txt
++ mawk '{printf("%.0f",$1/1000)}' /sys/class/thermal/thermal_zone0/temp
+ tmp_chk=58
+ '[' '' == test ']'
+ '[' -z 58 ']'
++ echo 58 '>=' 60
++ bc -l
+ status_warn=0
++ echo 58 '>=' 75
++ bc -l
+ status_alarm=0
+ [[ 0 == 0 ]]
+ [[ 0 == 0 ]]
+ exit 0

***** When temperature is in warning zone *****

root@MonHost:~# bash -x /usr/local/bin/temp_mon
+ tmp_warn=50
+ tmp_alarm=75
+ tmp_unit=C
+ FROM_NAME='PCEng Lab'
+ FROM_ADDRESS=PCEng@domain.com
+ TO_NAME='Lab'
+ TO_ADDRESS=lab@domain.com
+ MAIL_FILE=/tmp/mail_tmp.txt
++ mawk '{printf("%.0f",$1/1000)}' /sys/class/thermal/thermal_zone0/temp
+ tmp_chk=58
+ '[' '' == test ']'
+ '[' -z 58 ']'
++ echo 58 '>=' 50
++ bc -l
+ status_warn=1
++ echo 58 '>=' 75
++ bc -l
+ status_alarm=0
+ [[ 1 == 0 ]]
+ echo 'From: "PCEng Lab" <PCEng@domain.com>'
+ echo 'To: "Lab" <lab@domain.com>'
+ '[' 0 -eq 1 ']'
+ '[' 1 -eq 1 ']'
+ echo 'Subject: Temperature warning'
+ echo 'Content-type: text/plain; charset=utf-8'
+ echo 'Content-Language: en'
+ echo 'X-Auto-Response-Suppress: OOF'
+ echo ''
+ echo 'CPU temperature is High!'
+ echo 'Current temp: 58 °C'
+ '[' '' == test ']'
+ echo ''
+ echo '--- '
+ echo 'Sent by temp_mon script'
+ echo ''
+ cat /tmp/mail_tmp.txt
+ sendmail -f PCEng@domain.com lab@domain.com
+ logger 'temp_mon: high CPU temperature: 58 °C, mail sent'
+ rm /tmp/mail_tmp.txt

***** When temperature is in alarming zone *****

root@TestHost:~# bash -x /usr/local/bin/temp_mon

+ tmp_warn=50
+ tmp_alarm=55
+ tmp_unit=C
+ FROM_NAME='PCEng Lab'
+ FROM_ADDRESS=PCEng@domain.com
+ TO_NAME='Lab'
+ TO_ADDRESS=lab@domain.com
+ MAIL_FILE=/tmp/mail_tmp.txt
++ mawk '{printf("%.0f",$1/1000)}' /sys/class/thermal/thermal_zone0/temp
+ tmp_chk=58
+ '[' '' == test ']'
+ '[' -z 58 ']'
++ echo 58 '>=' 50
++ bc -l
+ status_warn=1
++ echo 58 '>=' 55
++ bc -l
+ status_alarm=1
+ [[ 1 == 0 ]]
+ echo 'From: "PCEng Lab" <PCEng@domain.com>'
+ echo 'To: "Lab" <lab@domain.com>'
+ '[' 1 -eq 1 ']'
+ echo 'Subject: Temperature ALARM'
+ echo 'X-Priority: 1 (Highest)'
+ echo 'X-MSMail-Priority: High'
+ echo 'Importance: High'
+ echo 'Content-type: text/plain; charset=utf-8'
+ echo 'Content-Language: en'
+ echo 'X-Auto-Response-Suppress: OOF'
+ echo ''
+ echo 'CPU temperature is High!'
+ echo 'Current temp: 58 °C'
+ '[' '' == test ']'
+ echo ''
+ echo '--- '
+ echo 'Sent by temp_mon script'
+ echo ''
+ cat /tmp/mail_tmp.txt
+ sendmail -f PCEng@domain.com lab@domain.com
+ logger 'temp_mon: high CPU temperature: 58 °C, mail sent'
+ rm /tmp/mail_tmp.txt
2 Likes

Thank you so much @Joulinar for your quick help to fix the formatting. It is great now. When you get chance, do review the comments as I will then post this as tutorial and then slowly add few other simple tutorials as well. All practical information from my side, no fluff.

It might be good to explain the email setup as you are using sendmail within your script. Why? Because this is not something available on a stock DietPi system. Maybe it could be included or become an own tutorial? Just as an idea :slight_smile:

Great idea and I agree. I will explain the DMA and sendmail part also. I plan to do some simple tutorials and let us widen the scope by slowly adding generic Linux and Python content as well. I am trying to learn and as I learn, I will share.

I will do couple of tutorials over this weekend for sure. Have a great and restful weekend for now.

Take your time. no need to rush. We all do this on our spare time as voluntary.

Okay my friend @Joulinar , my first tutorial is uploaded with some more comments and of course I have the same formatting issue. When you get chance, on Monday, please fix it and also advise me as to how really I should upload the code in the format. I did see an option for Upload button, but I was not sure of exact steps. Maybe a small post from your side that can be made sticky will help everyone.

PS: Just noticed, that I have been adding these messages in the original thread and not direct message to you. After you have reviewed it, I hope, you can simply delete these unwanted last few messages out of the thread so that others dont have to go thru this clutter.