Cool Music

I purchased some Klipsch AW-400 outdoor speakers for a good price and mounted them on my garage (which is a separate building.) To drive them, I bought a HifiBerry AMP+ and a Raspberry Pi 3B+. I’d been using Max2Play for music streaming but came across Volumio and I prefer it. The web interface is so much better. Also, it makes it really easy to share the USB hard drive I use as a media server on another Raspberry Pi.

Volumio works fine right out of the box and the set up instructions on their site are very easy to follow. The problem I have is that since I live in the desert and have my Raspberry Pi on a shelf high in my garage, it overheats occasionally. I bought heat sinks for it but that didn’t solve the heat problem.

My solution was that I bought a USB powered fan on a goose neck. I got this one but any USB-powered fan, NOT one that charges a battery via USB, will work. I suppose I could just let the fan blow in the Raspberry Pi constantly, but where’s the fun in that? I figured that if I can read the temp, I turn the fan on and off as needed. Yes I can. Yes I did and here’s how.

I found that the CPU temperature is stored in /sys/class/thermal/thermal_zone0/temp as milidegrees Celsius, which means 45000 is 45° C.

I found uhubctl at GitHub which allows Linux in some applications to control some smart USB hubs. Fortunately, a RPi 3B+ is the right application and its USB ports are the right kind of smart USB hubs. To get it to work, it has to be download and compile first.

Volumio ships as a fairly stripped down version of Debian. To add the missing standard software, run:

sudo apt-get update && sudo apt-get -y upgrade

Next, install gcc, make, and a USB library:

sudo apt-get install gcc
sudo apt-get install make
sudo apt-get install libusb-1.0

To download the uhubctl command, we need to install git:

sudo apt-get install git

At this point we should be able to download and compile uhubctl:

git clone git://github.com/mvp/uhubctl
cd uhubctl
make

uhubctl is now ready to use. Next we need to find which port the fan is plugged in to. To list the ports run:

sudo ./uhubctl

You should see an output something like:

Current status for hub 1-1.1
  Port 1: 0503 power highspeed enable connect [0424:7800]
  Port 2: 0100 power
  Port 3: 0100 power
Current status for hub 1-1 [0424:2514]
  Port 1: 0503 power highspeed enable connect [0424:2514]
  Port 2: 0100 power
  Port 3: 0100 power
  Port 4: 0100 power
Current status for hub 1 [1d6b:0002 Linux 4.19.118-v7+ dwc_otg_hcd DWC OTG Controller 3f980000.usb]

In this case, my fan is plugged into hub 1-1 port 2 but it doesn’t show up.

To turn the right port off, you need to be specific about which hub and which port. Plug in the fan, and hunt for it by turning ports off one at a time till the fan stops.

The command to turn off my fan is:

sudo ./uhubctl -l 1-1 -p 2 -a off

To turn it back on:

sudo ./uhubctl -l 1-1 -p 2 -a on

And to get the current status:

sudo ./uhubctl -l 1-1 -p 2

Experiment with it and make sure you can control your fan. Next, we’ll create a script to switch the port on or off depending on the temperature of the CPU:

sudo nano coolme.sh

Copy and paste this code:

#!/bin/bash
# This is where the raw cpu temp is stored
# Could use vcgencmd measure_temp but it returns
#   a formatted string. Messy.
cpu=$(</sys/class/thermal/thermal_zone0/temp)
# This is the limit I set. Change as necessary
limit=70000
# To find which port your fan is plugged in run:
#  sudo ./uhubctl May need to experiment turning
#  them off to find it.
port_state=$(sudo /home/volumio/uhubctl/uhubctl -l 1-1 -p 2)
if [ $cpu -gt $limit ]; then
# We're overtemp. Turn on fan if necessary
  if [ $(expr substr + "$port_state" 79 6) == "off" ]
  then
  # Port was off, turn it on
    echo "Overtemp: CPU raw temp is $cpu 'C at $(date)"
    /home/volumio/uhubctl/uhubctl -l 1-1 -p 2 -a on
    printf " -----\n"
  fi
else
# We're undertemp. Turn fan off if necessary
  if [ $(expr substr + "$port_state" 79 6) == "power" ]
  # Fan is on, turn that baby off
  then
     echo "Undertemp: CPU raw temp is $cpu 'C at $(date)"
     /home/volumio/uhubctl/uhubctl -l 1-1 -p 2 -a off
    printf " -----\n"
  fi
fi

If you’re using a different hub, you may need to change

  if [ $(expr substr + "$port_state" 55 6) == "off" ]

What it is saying is to start at character 55 and read the following 6 characters. If you use hub 1-1.1, that will throw that number off. To find it, I used the following command and adjusted the first number till I got “power” or “off” with no space in front of it.

port_state=$(sudo /home/volumio/uhubctl/uhubctl -l 1-1 -p 2) && echo $(expr substr + "$port_state" 55 6)

I set the limit to 70° C because the overtemp warning happens at 80° C. Adjust the limit as you see fit.

To make the script executable, run

sudo chmod 755 coolme.sh

Now test the script. Change the limit to make it turn on the fan and then change it back to turn it off. Make sure it works. To run the script:

sudo ./coolme.sh

If you don’t use sudo, uhubctl won’t work correctly. Once you’re satisfied with the script, we need to add a cron job to execute it every five minutes. Like I said, Volumio is pretty stripped down, so we need to install cron:

sudo apt-get install cron

Add the script to the cron jobs:

sudo crontab -e

If you don’t use sudo, you’ll only add the script for the user and we need root access for this thing to work.

Add the following line to the bottom of the file:

*/5 * * * * /home/volumio/uhubctl/coolme.sh >> /home/volumio/log.txt

I included the log file because I want to see that it works. The way the script is written, it will only produce output when it turns the fan on or off so the log file won’t fill up too quickly.

If you want to make sure the cron jobs are working, you can also add:

* * * * * echo “crontest $(date) $(whoami)” >> /tmp/crontest.txt

Don’t forget to remove it or it will create a pretty big log file in a few days.

That should do it. My experience has been that the fan cools the RPI down pretty quickly. Here’s an example of my log file:

Overtemp: CPU raw temp is 70370 'C at Sun Jun 24 22:45:01 PDT 2018
Current status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0000 off
Sent power on request
New status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0100 power
 -----
Undertemp: CPU raw temp is 49388 'C at Sun Jun 24 22:50:01 PDT 2018
Current status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0100 power
Sent power off request
New status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0000 off
 ------
Overtemp: CPU raw temp is 70370 'C at Mon Jun 25 15:40:01 PDT 2018
Current status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0000 off
Sent power on request
New status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0100 power
 -----
Undertemp: CPU raw temp is 52616 'C at Mon Jun 25 15:45:01 PDT 2018
Current status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0100 power
Sent power off request
New status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 2: 0000 off
 -----

You can see that it overheated at 70.3° C, kicked the fan on and in 5 minutes when it ran again, the temperature was down to 49.3° C so it turned it back off. While the outside temp remains high, it does oscillate on and off but we’re doing that well below the overheat limit so we should be good.

Print This Post Print This Post

13 Comments

  • uhubctl author here.
    Nice work, well done with this project!

    Few tips:
    * You don’t need to be root or use sudo to make uhubctl work if you configure udev properly, read more here: https://github.com/mvp/uhubctl/blob/master/README.md#linux-usb-permissions.
    * It’s `git clone`, not `git close` :)
    * If you use `sudo make install`, uhubctl will be installed system wide as “/use/sbin/uhubctl, so you won’t need to use full path in most cases.
    * Some newer Linux distros (e.g. Ubuntu 18.10 or newer, and perhaps Raspbian) include uhubctl as a package, so just `sudo apt install uhubctl` may install it into your system already without dealing with gcc, git or make.
    * You can get port state with:
    port_state=$(uhubctl -l 1-1 -p 2 | tail -1 | awk ‘{print $4}’)
    It will be string `off` or `power`.

  • Thank you mvp!! I appreciate your corrections and catching my typo. I’ll fix them.

  • Congratulations for your work!
    When you say “turning ports off one at a time”, do you mean that you can control the _single_ ports with uhubctl? I’ve read that some Raspberries can only perform a poweroff on the whole hub (a “ganged” poweroff), rather than acting on the individual ports.

  • Yeah Rocky, that’s what I mean. I thought about just turning off all the USB ports but it was more fun to turn off just the one I wanted.

  • Hi,
    I am newby in this field of area (linux, volumio, etc…)…so there is a lot of secrets for me.
    I am using a Volumio for some days and I was looking for some cooling solution – like you.
    I was happy to find your webpage.
    I followed your instruction but my progres finished at the line 23 of the coolme.sh file with sentence:
    ./coolme.sh: line 23: [: ==: unary operator expected

    I dont know how to continue.

    Could you please help me?

    thank you

    Michal

  • Hey Michal, I’m glad to help. I think the problem may be that $cpuf is empty. Since you’re from Europe, I’m guessing you’re wanting to use Celsius rather than convert to Fahrenheit. If that’s the case, at line 23 you should replace $cpuf with $cpuc. Let me know if that works. If it doesn’t please post what you have at line 23 and we’ll see what we can figure out.

  • Hi Tim,
    sorry, I was two days off.

    I found it! I had to change your code from:
    $(expr substr + “$port_state” 74 6)
    to
    $(expr substr + “$port_state” 54 6)
    And it works… :)
    thank you for your inspiration do do this project.

    But I have another question:
    I am not able to perform per-port power switching as you wrote.
    Using the command: uhubctl -l 1-1 -p 2 -a off are all 4 usb powered down, not only the one with the fan.
    Maybe I misunderstood something…

    thank you.

    Michal

  • Glad you figured it out, Michal! Good work. As for the command powering off all the ports, I’m not sure what’s going on. The command you’ve typed looks right to me. You might consider contacting MVP, the uhubctl author at Github and see if he has any insights. He did the first comment on this post and linked to the project at Github so I think that would be the best place to get in touch with him.

  • Hello Tim,

    I hope that this is still being checked and read.

    SCOPE:

    I am looking for a solution powering on my usb powered speakers only when music is played.

    MY SETUP:
    I have 5 Raspberry Pis setup as multiroom audio system using mopidy and snapcast.

    4 Raspberry Pis 3B+ of them have
    – PoEHAT (so only a network cable is needed)
    – DAC with Broadcom BCM 2835 chip
    – Speaker Pebble from Creative (usb powered and 3.5 mm audio connection to the DAC)
     
    CURRENT SCENARIO:

    I wanted to power off automatically those speakers during the night, because of the LED (bright green).

    I found a solution from github using uhubct and I have done the installation and configuration for power off the USB ports (currently all 4 them) via script using cron between 11:30 p.m. and 6:30 a.m. that same github had  under  “Notable projects using uhubctl” your post “USB fan for Raspberry Pi” listed.
     
    the only way to power off the speaker with uhubct using the following command:
    uhubctl -l 1-1 -p 2 -a off. I can see on port one Power highspeed enabled, but it does not matter where I plug in the USB cable for powering the speakers. I can turn off port 1, 3 and 4, but only with p 2 it is powering off the speakers. when I try uhubctl -l 1-1.1 -p 1 -a off, I have to forcefully reboot the pi

    As described in the section SCOPE I want to go further and have the command for turning power on in that one USB Port (all other 3 should be powered off permamently) when music is being played. and when no music is played for 5 minutes to power off that USB Port. I assume that would be possible have it triggered in conjunction with ALSA or DAC activities?

    Any inputs are appreciated.

    Thanks

  • Hey Gerald, yeah, I keep up with this blog. If you can find something in the system that you can read, like I did with the CPU temp, when the music is playing, you could use that as the trigger to turn off the speakers. I’m not familiar with the apps you’re using so I can’t help there.

    My concern is with turning the speakers on when the music starts. I think that, at best, there would be a momentary delay and you’d miss the first beat or two. If there were some setting or plug in you could use with Mopidy that would send a signal to turn on the speakers and delay the music starting for a beat or two, I think that’s the way to go.

    I hope this helps get you pointed in the right direction. Sorry I couldn’t be more specific.

  • Hello, nice script, but I can’t run it automatically… only could run the command “sudo ./coolme.sh manually, I could start or stop my usb fan, depending on the temperature. Any suggestion? Thank you a lot.

  • Hi Nuno, sorry this took me so long to get back to you. I was having problems with comments on my blog, I couldn’t post!

    Anyway, did you run that part to test to see if your cron jobs are running?

    Add the script to the cron jobs:

    sudo crontab -e

    at the bottom add:

    * * * * * echo “crontest $(date) $(whoami)” >> /tmp/crontest.txt

    Don’t forget to remove it or it will create a pretty big log file in a few days.

    Then you can check the crontest.txt and make sure your cron jobs work.

    Let me know!!!

  • […] on it so I could reuse my USB-powered fan and hopefully keep it cool. I won’t be able to control it like I did with the Raspberry Pi but I’m working on some possibilities to do that. Might […]

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>