Thursday, April 20, 2017

IR Volume Control for Sonos Connect AMP

Update: I originally did this using LIRC version 0.9.0.  Since then apt-get has been updated to use LIRC 0.9.4 which changed a lot and broke the original instructions.  I've updated this to work with LIRC 0.9.4. 

Our house has a finished basement with speakers built into the ceiling and walls.  The speaker wires run to a common spot behind a mount for a flat-screen TV.  I already own a Sonos Connect Amp and I wanted to use it to play the audio from the TV on the builtin speakers.  Acoustically, everything is great, but adjusting the volume is less than ideal.  Because the Connect Amp does not have an IR receiver, a universal remote is not able to change the volume.  The only way to change the volume is to use the Sonos app on my smartphone or by reaching into the equipment cabinet and physically pressing the volume buttons on the Connect Amp (I know, I know, first world problems...).  What I really wanted was to control the volume from the same remote that I was using to change channels.

My solution was to use a Raspberry Pi Zero W with an IR receiver attached to convert the signals from the TV remote to HTTP calls on my local network.  The HTTP calls then control the Sonos Amp in the same way the smartphone app does.


I do realize Sonos makes a speaker specifcally for a TV, the Playbar, and it is controllable from an IR remote.  But for my application, I already had the Connect Amp and I wanted to reuse the builtin speakers.

It took me few days of trial and error to get things working.  There is a lot of online content for using the Raspberry Pi as an infrared receiver.  Unfortunately, there is a lot of contradictory information in that content.  I'm not a "linux guy", so I can't claim that I fully understand all of the linux commands required to make this work.  BUT, my hope is to provide enough detail for others to recreate this project.

Hardware List

  • Sonos Connect Amp
  • Raspberry Pi Zero W and case
  • USB Power Supply
  • IR Receiver Module TSOP38238
  • Universal Remote
  • Micro SD card (I used an 16 GB card)
  • Micro USB On-The-Go (OTG) Hub
  • Micro HDMI to Standard HDMI Adapter
  • Monitor, Keyboard & Mouse

Software for this Project

The Hardware Part

Get out your soldering iron (or your jumper wires)!  You will need to connect the IR Receiver module to the GPIO header of your Pi.  There are only 3 connection, so this is super easy.





Raspberry Pi Zero W wired with IR Receiver

Raspberry Pi Setup

I used a Raspberry Pi Zero W because it was the smallest Pi and it had built in Wi-Fi.  I haven't tried it, but I think a Raspberry Pi 2 or 3 would work as well.  If you are going to attempt this with a version 2, you'll need a USB Wi-Fi adapter.

A note about Nano

Many times in these instruction I make use of a linux text editor called nano.  Nano isn't pretty, but it gets the job done.  After making edits with nano, you will save by hitting Control-X, they Y to the question about saving the buffer and finally Enter to overwrite the existing file.  I'm mentioning this now so I can shorten the instructions.

Okay, let's get started: basic setup first

  1. First get your Pi up and running with Noobs OS.  This will involve connecting your Pi to a monitor with the micro HDMI adapter and cable, and connecting a mouse & keyboard to USB with the USB OTG hub.  Follow this tutorial if you need help with that.
  2. At this point you should be staring at the desktop of a running Raspberry Pi.  If it's not already, get it connected to the internet.  Config the Wi-Fi... or plug in the ethernet cable if you decide to go that route.
  3. Open up a terminal window, enter this and wait while things get updated:

    sudo apt-get update

Install Node.js

Steven de Salas has done a great job of making this a relativity painless procedure by creating some scripts specifically for installing Node.js on a Pi Zero.  In the terminal type this (all as one line):

wget -O - https://raw.githubusercontent.com/sdesalas/node-pi-zero/master/install-node-v7.7.1.sh | bash

This may take a minute or two and don't worry about any unlink warning you may see.

Install the Sonos HTTP API

This is the service that will allow you to interact with the Sonos devices on your network.  Kudos to the creator, Jimmy Shimizu, for making such a great piece of software!

wget http://github.com/jishi/node-sonos-http-api/archive/master.zip

unzip master.zip

You should now have a folder called node-sonos-http-api-master in your /home/pi directory. You can use the ls command to confirm.

Now get the Sonos HTTP API server up and running with all of the dependencies.

cd node-sonos-http-api-master

It will probably take a few minutes for this next step to finish.

npm install --production

npm start

The API should be running at this point.  You can check this by getting the state of your Sonos speaker.  Open up a second terminal and use the the following command. Replace <your speaker> with the name of your Sonos speaker (leaving out the brackets).

curl http://localhost:5005/<your speaker>/state

For example, my Connect Amp is named "Basement".  Here is what it looks like: 



As you can see, a bunch of details about the state of the Basement speaker is returned.

You can stop the HTTP API process my hitting Control-C in the terminal window where you typed npm start.

Install and Configure LIRC

LIRC is the software that decodes the IR signals.  There is a lot of information out there about LIRC and it seems that it can do a lot.  Unfortunately, all that information is very confusing for a noob who just wants to control his Sonos.  This is the most complicated part of the entire project.  Double check your work, as it is easy to make a typo.
  1. Install LIRC

    sudo apt-get install lirc
     
  2. Assign the GPIO we are using

    sudo nano /etc/modules

    Add this text to the bottom of the text that is already there and exit/save:

    lirc_dev
    lirc_rpi gpio_in_pin=18
  3. Next, edit some boot settings
    sudo nano /boot/config.txt
    Towards the end of the file, there is a line that looks like this: #dtoverlay=lirc-rpi change it to this: dtoverlay=lirc-rpi,gpio_in_pin=18
  4. Restart you Raspberry Pi
  5. Set up the LIRC driver.

    sudo nano /etc/lirc/hardware.conf

    This will open an empty file.  Paste the following text into the editor and exit/save.

    # /etc/lirc/hardware.conf
    #
    # Arguments which will be used when launching lircd
    LIRCD_ARGS="--uinput"

    # Don't start lircmd even if there seems to be a good config file
    # START_LIRCMD=false

    # Don't start irexec, even if a good config file seems to exist.
    # START_IREXEC=false

    # Try to load appropriate kernel modules
    LOAD_MODULES=true

    # Run "lircd --driver=help" for a list of supported drivers.
    DRIVER="default"
    # usually /dev/lirc0 is the correct setting for systems using udev
    DEVICE="/dev/lirc0"
    MODULES="lirc_rpi"

    # Default configuration files for your hardware if any
    LIRCD_CONF=""
    LIRCMD_CONF=""
  6. Now, because of some major changes to the way LIRC 0.9.4 works, we need to convert our configuration to the new setup.

    cd /usr/share/lirc

    sudo ./lirc-old2new
  7. Check if the IR receiver works.

    cd

    mode2 -d /dev/lirc0

    Point your remote at the Pi and press some buttons.  You should see a ton of timing data get printed.  Hit control-C to exit mode2.

IR Code Capture

  1. The next few steps involve using your remote control.  Because there is no actual IR codes for a Sonos Connect Amp, I decided to use the codes from a Sony Amplifier/Receiver instead. You don't have to use Sony, especially if it will interfere with some Sony hardware that you already have.  The point is that I picked a well known brand that didn't do anything crazy with its IR codes.  So, get your universal remote set up and ready.
     
  2. Capture the IR codes from your remote.  From a terminal, type the following command:

    sudo service lircd stop

    sudo irrecord -d /dev/lirc0 ~/lircd.conf

    This will start up a utility to analyse your remote control's IR signal and capture the specific button codes.  Follow the directions carefully.  The first part of process is to press a bunch of buttons on the remote while aiming at the IR receiver.  After it is happy with that it will want to record the specific button codes.

    When asked for a file/remote name, you can use "sonos".

    We'll be recording 3 different buttons: volume up, volume down and mute.  When asked, use these names for the buttons:

    KEY_VOLUMEUP
    KEY_VOLUMEDOWN
    KEY_MUTE


    After using irrecord, we should have a file with codes for volume and mute.
     
  3. Move the file to where is needs to go

    sudo mv sonos.lircd.conf /etc/lirc/lircd.conf.d/sonos.lircd.conf
  4. Restart LIRC

    sudo service lircd start
     
  5. Check to see if your Raspberry Pi is actually picking up the codes

    irw

    Now point your remote at the Pi and press the volume and mute buttons.  You should see some text pop up like this. (Hit Control-C to exit the irw tool)

Connecting button to actions

Now that LIRC is happily decoding IR signals, we need to make a file to execute commands when a signal is detected.  In this case we'll be hitting the Sonos HTTP API.
  1. Make the lirc command file:

    sudo nano ~/.config/lircrc

    Add this text to the empty file and exit/save.  Make sure to replace <your speaker> with the name if your Sonos device. Again, leave out the brackets.

    begin
    prog=irexec
    button=KEY_VOLUMEUP
    config=curl http://localhost:5005/<your speaker>/volume/+1
    end
    begin
    prog=irexec
    button=KEY_VOLUMEDOWN
    config=curl http://localhost:5005/<your speaker>/volume/-1
    end
    begin
    prog=irexec
    button=KEY_MUTE
    config=curl http://localhost:5005/<your speaker>/linein
    end
    begin
    prog=irexec
    button=KEY_MUTE 
    config=curl http://localhost:5005/<your speaker>/togglemute
    end
  2. Save and exit

Line-In Work-around

Most of the lircrc file should be easy to follow. There is a specific HTTP API call being associated with each button: volume up, volume down and mute. But what is the deal with the call to linein and the mute button?

Well, like every other Sonos owner, I use this device to play more than just TV audio. I use it for music just as often as I use it for TV. The problem is, if I last used it for music then I would need to use the app to set it back to line-in. My solution was to re-select line-in anytime the mute button with pressed. So if I turn on the TV and I hear no audio, I just hit the mute button once or twice and it start working.

A better solution would be to link the TV's power button code to the line-in selection. Unfortunately, I have a Samsung smart TV that uses an RF remote instead of infrared. The Raspberry Pi can't detect the RF signals. So I'm stuck with the mute button trick for now.

Getting everything to run at startup (almost there!)

Do get all this to work every time you power up the Pi, we need to edit a boot file.

sudo nano /etc/rc.local

Add this text right before exit 0

su pi -c 'node /home/pi/node-sonos-http-api-master/server.js > /dev/null &'
sleep 2
sudo /usr/sbin/lircd -d /dev/lirc0 > /dev/null &
sleep 2
su pi -c 'irexec > /dev/null &'


Save and exit.

Because we'll be running LIRC on startup, we need to disable the service from running.

sudo systemctl disable irexec.service

Finally, restart the Raspberry Pi and you be able to control your Sonos from your remote!

13 comments:

  1. shouldn't your redirects be to /dev/null not from it? Ie you have '< /dev/null' - which is to read from /dev/null - I think you meant this: '> /dev/null' to surpress any output?

    ReplyDelete
  2. You are correct, thank you for pointing that out! I've edited the text to fix the error.

    ReplyDelete
  3. I new to this but I cannot get any of the LIRC commands such as
    sudo /etc/init.d/lirc stop
    sudo /etc/init.d/lirc restart

    to work. I always get
    /etc/init.d/lirc: command not found

    Any idea what I doing wrong?

    ReplyDelete
    Replies
    1. I had a typo, it's supposed to be
      sudo /etc/init.d/lircd stop

      I've updated the text in the post but now I'm running into some other problems with this latest version of LIRC. I'm looking into it.

      Delete
    2. You are correct, LIRC 0.9.4 broke a lot of stuff. I've modified the instructions, hopefully it works for you.

      Delete
  4. I do see that LIRC was updated in May. Let me rerun these steps on a new Pi Zero and see if I run into problems.

    Apologizes for taking so long to respond. My comment notifications were going to the wrong place.

    ReplyDelete
  5. This looks great but I am struggling to get past the same point as JDS73 with the same errors, do you have any suggestions to get things working. Any help appreciated

    ReplyDelete
    Replies
    1. It looks like I had some typos. I was missing a 'd' on lirc It was supposed to be
      /etc/init.d/lircd start

      I fixed the typo in the text but there appears to be some other problem with the latest version of LIRC. I'm looking into it.

      Delete
    2. Try it now. These instructions should work with the new version of LIRC now.

      Delete
    3. Hi I know this is an old post, but I am starting to pit together a little IR receiver for my connect:amp, unfortunately it seems like the new kernel does not support LIRC but instead natively supports IR signals. Amy advice on how to get this to work on the latest Raspberry Pi OS?

      Delete
    4. Sorry, I haven't done much with this since the original build. Once I got it working, it went into my media cabinet and that was it. I used Raspbian 8 (Jessie) for this. If you are building a single purpose device like I did, then there is no harm in using the older version OS.

      Delete
  6. Had the same issue as JDS73 but eventually realised that my pi zero w had shipped with noobs v2.4.5 in which lirc has changed somewhat. I have downgraded noobs to v2.4.0 as stated at the top of this document and it works perfectly. Thanks you for this fantastic tutorial!

    ReplyDelete
  7. This is great! I however had problems getting it to work by moving the conf file to the librc.d/ folder. Instead I moved it to the parent folder are overrode the librcd.conf which worked great

    ReplyDelete

IR Volume Control for Sonos Connect AMP

Update: I originally did this using LIRC version 0.9.0.  Since then apt-get has been updated to use LIRC 0.9.4 which changed a lot and bro...