Friday, April 4, 2014

LED VU Meter using the MyPiShop 8x8 PiMatrix

One of the first addons I got for the Raspberry Pi was the 8x8 PiMatrix, which was a lot of fun to play with. Who doesn't enjoy making LEDs blink? I also wanted to use the PiMatrix for some sort of music visualization, but I'll get to that in a little bit. 

Below are the links for the PiMatrix, assembly instructions and the original example code. The expansion board under the removable LED matrix is also usable by itself for an additional 16 bits of I/O:

http://mypishop.com/Pi%20MATRIX.html
http://w8bh.net/pi/PiMatrix1.pdf - Assembly
http://w8bh.net/pi/PiMatrix2.pdf - Test Routines
http://w8bh.net/pi/PiMatrix3.pdf - Toolkit
http://w8bh.net/pi/PiMatrix4.pdf - Scrolling Text

I rewrote Bruce E. Hall's sample code library into a more object-oriented form, creating a LEDMatrix object and making the variables and functions in the original into properties and methods of that object.  I also changed a few methods to accept more parameters such as variable delay times, so the timing and speed of individual routines can be changed without affecting everything. 

All my code is at the Bitbucket link below:
https://bitbucket.org/rvalbuena/pimatrix/src

This example makes an explosion pattern that starts in the center and moves out to the four corners of the display:
from LEDMatrix import LEDMatrix
from time import sleep

delay=0.06

if __name__ == '__main__':
    lm = LEDMatrix()
    lm.ORIENTATION = 0
    // 0 = power jack on left, 90 = power jack down,
    // 180 power jack on right, 270 power jack on top
    lm.enableLEDS()
    try:
        while True:
            lm.setPattern(0b00011000,0b00011000)
            sleep(delay) 
            lm.setPattern(0b00111100,0b00111100)
            sleep(delay) 
            lm.setPattern(0b01100110,0b01100110)
            sleep(delay) 
            lm.setPattern(0b11100111,0b11100111)
            sleep(delay) 
            lm.setPattern(0b11000011,0b11000011)
            sleep(delay)
    except (KeyboardInterrupt, SystemExit):
        lm.turnOffLEDS()
        #lm.disableLEDS()
    finally:
        # ensure that the leds turn off
        lm.disableLEDS()

I use a similar script as a visual indication that the Pi had lost wifi connection; when my wifi check script fails to ping my router, it launch a python script that causes lots of blinking to get my attention.

So now I wanted to use the PiMatrix for some sort of music visualization, but I also wanted it to work with my existing mpd setup instead of using other players and music library management as in the other examples found online. From using the ncmpccpp client I also knew that mpd could already create a fifo output file usable for this purpose:

https://wiki.archlinux.org/index.php/ncmpcpp#Enabling_visualization
http://ncmpcpp.rybczak.net/

Fortunately, there was an example on stackoverflow for doing almost exactly what I needed. In XBMC, I prefer the simpler oscilloscope music visualization instead of the more complex ones, so I'm OK with just displaying the volume peaks instead of the fancier (and more CPU-intensive?) spectrum analyzer.

In the linked code example the mpd.fifo is read using audioop functions:

http://stackoverflow.com/questions/21762412/mpd-fifo-python-audioop-arduino-and-voltmeter-faking-a-vu-meter


Below is the complete code for the led_vu_meter.py which is also in the Bitbucket repo linked above. Basically, here's what happens:

* The peak stereo volume is read from the fifo
* That value is divided by some "magic number" I arrived at by trial and error to get a 0-to-8 to correspond to a row index, and that new value is appended to the head of a deque
* Loop through the deque and light up the individual LED corresponding to the loop index and value stored in the deque
* Pop the oldest value off the tail end of the deque
The visual effect should be of a "wave" moving left to right.
# # "VU" meter display based on code from Stackoverflow # * http://stackoverflow.com/questions/21762412/mpd-fifo-python-audioop-arduino-and-voltmeter-faking-a-vu-meter # using the PiMatrix LED # * http://mypishop.com/Pi%20MATRIX.html # * http://w8bh.net/pi/PiMatrix1.pdf # * https://bitbucket.org/rvalbuena/pimatrix import os import audioop import time import errno import math from collections import deque from LEDMatrix import LEDMatrix if __name__ == '__main__': our_pid = os.getpid() print our_pid #Open the FIFO that MPD has created for us #This represents the sample (44100:16:2) that MPD is currently "playing" fifo = os.open('/var/tmp/mpd.fifo', os.O_RDONLY) # note: /var/tmp is mapped to RAM in /etc/fstab lm = LEDMatrix() # the "fastSet" routines ignore orientation # but I'm using the Pi hdmi-connector side down and apparently that's "this side up" lm.ORIENTATION=0 lm.enableLEDS() try: # create our buffer for the display values # there are probably faster data structures # but this seems OK for now vol_queue = deque() # initialize it with zeros for p in range(0,7): vol_queue.append(0) while 1: try: rawStream = os.read(fifo, 4096) except OSError as err: if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK: rawStream = None else: raise if rawStream: # I'm really only interested in the stereo peak #leftChannel = audioop.tomono(rawStream, 2, 1, 0) #rightChannel = audioop.tomono(rawStream, 2, 0, 1) stereoPeak = audioop.max(rawStream, 2) #leftPeak = audioop.max(leftChannel, 2) #rightPeak = audioop.max(rightChannel, 2) #leftDB = 20 * math.log10(leftPeak) -74 #rightDB = 20 * math.log10(rightPeak) -74 # 'magic numbers' that will produce 0..8 for the led matrix # note that reducing mpd volume via software mixer (mpc volume XX) # will also reduce these values, so better to use the speaker vol knobs peak = (stereoPeak/4000)-1 #peak = (stereoPeak/3800)-1 vol_queue.append(0 if (peak < 0) else peak) # print vol_queue for m in range(0,8): # this produces a decent sine wave like pattern # fastSetLED ignores orientation # lm.fastSetLED(vol_queue[m],m) lm.setLED(vol_queue[m],m) # removing this pause actually produces a better looking # (more filled in) wave and does not seem to kill performance # (I think there are "sleeps" in some of the LED Matrix routin$ # time.sleep(0.01) # remove from the head of the deque vol_queue.popleft() # clear the display so that when we reach the end of the fifo # (mpd stops playing/reaches the end of the playlist) # we don't have anything left on the display lm.turnOffLEDS() except (KeyboardInterrupt, SystemExit): lm.turnOffLEDS() #lm.disableLEDS() finally: # ensure that the leds turn off lm.disableLEDS()

I've since read that a deque is not the fastest data structure to use for this, but it behaves exactly the way I needed it to without my having to jump through hoops to manage the list of values read from the fifo, and the performance seems fine.

Another thing to note is that in my mpd.conf I changed the location of my mpd.fifo to /var/tmp/mpd.fifo (/var/tmp/ is mapped to RAM in my fstab); I believe the example config in the "Enabling visualization" link above uses /tmp/mpd.fifo instead. So what's defined in your mpd.conf needs to match the file the script below will try to read.

Since this is on my dedicated print server mpd music player, I have the script auto-start by adding it to /etc/rc.local

I've watched the Pi's CPU usage while this script is running and it doesn't seem to be bad - usually around 25% and sometimes spiking to around 40% for very short periods. In fact I've been ssh'ed into the Pi and editing other code in nano while it's running and it's been very responsive.

One final problem to solve is having different scripts that can use the PiMatrix not interfere with each other. This led_vu_meter is always running, and as I mentioned above there's another script that gets triggered when the wifi connection goes down. Sometimes I've also accidentally started more than one instance of these scripts. The multiple processes sending different instructions to the LEDs at the same time is probably not a good thing, and at the very least leads to garbage pixels on the display. I'll probably resort to one "master" script that has logic to determine what should be displayed.

Saturday, November 30, 2013

Lubuntu on an old Toshiba Tecra M7 laptop/tablet convertible

Over the summer I got an old Toshiba Tecra M7 (made in 2006); this is one of those laptop/tablet convertibles, where the screen can be rotated around and locked face-up over the keyboard to be used as a tablet with a stylus. So I can have fun scribbling on the screen with Inkscape and other drawing programs, then flip the screen back around and still use it as a normal laptop. It's handling XBMC playback just fine, so I don't think it's that underpowered.

Lubuntu 12.10 is currently installed and seem to be working well. The built-in wifi card is only Wireless G, which isn't bad, but I'm trying out a Tenda W311MI "Pico USB Adapter" to get Wireless N, and it's averaging 70-80Mb/s. I'm concerned about losing or damaging this tiny little adapter when packing up the laptop, since it does stick out, and when I plugged in another USB device it stopped responding. So I may still eventually replace the internal wifi card, though it looks like major surgery.

To enable screen rotation to portrait mode when using it as a tablet, I did the following, combining instructions from Steve Welburn's post here, as well as some Ubuntu forum threads that I can't seem to find anymore.

I switched to the proprietary tested Nvidia drivers (launch Software Updater, click Settings and go to the Additional Drivers tab), restarted, and then generated an xorg.conf with the following command:
sudo nvidia-xconfig
Then edit the generated /etc/X11/xorg.conf and add the following to the last "Device" section
      Option  "RandRRotation" "true"
Check what the detected input devices for the stylus are:
xinput
This will produce a listing like the following: it's the Serial Wacom Tablet stylus and eraser that we want.


⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ PS/2 Mouse id=10 [slave pointer (2)]
⎜ ↳ AlpsPS/2 ALPS GlidePoint id=11 [slave pointer (2)]
⎜ ↳ Serial Wacom Tablet stylus id=12 [slave pointer (2)]
⎜ ↳ Serial Wacom Tablet eraser id=14 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
   ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
   ↳ Power Button id=6 [slave keyboard (3)]
   ↳ Video Bus id=7 [slave keyboard (3)]
   ↳ Power Button id=8 [slave keyboard (3)]
   ↳ AT Translated Set 2 keyboard id=9 [slave keyboard (3)]
   ↳ Toshiba input device id=13 [slave keyboard (3)]
The shell script is below, I put mine in /usr/local/bin and made a menu shortcut to it.
#!/bin/sh

# Find the line in "xrandr -q --verbose" output that contains current screen orientation and "strip" out current orientation. 

rotation="$(xrandr -q --verbose | grep 'connected' | egrep -o  '\) (normal|left|inverted|right) \(' | egrep -o '(normal|left|inverted|right)')" 

# Using current screen orientation proceed to rotate screen and input tools. 

stylus="Serial Wacom Tablet stylus"
eraser="Serial Wacom Tablet eraser"
mouse="PS/2 Mouse"
touchpad="AlpsPS/2 ALPS GlidePoint"

case "$rotation" in 
    normal) 
#    -rotate to the right 
    xrandr -o right 
    xsetwacom set "$stylus" rotate  CW 
    xsetwacom set "$eraser" rotate CW 
    ;; 
    right) 
#    -rotate to normal 
    xrandr -o normal 
    xsetwacom set "$stylus" rotate NONE 
    xsetwacom set "$eraser" rotate NONE 
    ;; 
esac
I wanted to use the "rotate" button built into the Tecra's monitor. After some Google searching I found that the following would display what keycodes were bound to it:
xev | egrep -o "keycode.*\)"
As it turns out, that button is bound to "Windows Key + 6". In order to trigger the hotkey, I had to edit my ~/.config/openbox/lubuntu-rc.xml and add the following at the end of the other "keybind" items:
    <!-- Screen rotation script -->
    <keybind key="W-6">
      <action name="Execute">
        <command>/usr/local/bin/rotateScreen.sh</command>
      </action>
    </keybind>
I don't think there's a global lubuntu-rc.xml so if you want to share customized hotkeys with other users on the same computer you have to update the lubuntu-rc.xml in their profile.

And that's it!

Saturday, July 20, 2013

Building PCManFM and LibFM from source to fix the network browsing bug in 1.1.0

One of the things I like about LXDE and Lubuntu is the PCManFM file manager. One of the few features it lacked was integrated file search. That's now build into version 1.1.0, which is installed by default in Lubuntu 13.04 and available as an update in 12.10.

However, this version also introduces a bug that breaks network browsing, and you won't be able to browse Samba shares on your network. I've read the fix is scheduled for the 1.1.1 release, but in this case it's very straightforward to compile the necessary files yourself from source, thanks to the clear instructions on the LXDE wiki.

One additional dependency that's not listed in the wiki is gtk-doc-tools, which the autogen.sh scripts will keep halting on. It's possible that it would have been installed as a recommended dependency by another package, but I usually use "apt-get install" with "--no-install-recommends" to prevent installing extra packages.

In compiling for Lubuntu 13.04, one of the package dependencies is also slightly different, you have to substitute libmenu-cache-dev for libmenu-cache1-dev from the wiki instructions.

Friday, June 14, 2013

Using Grails external config can only override values in the original Config.groovy?

When using an external config for a Grails 1.3.7 application, it appears that you are actually overriding properties defined in the project's /grails-app/conf files (like Config.groovy, DataSource.groovy). So if a given property exists but isn't explicitly initialized to some default value (such as connection pool sizing parameters in DataSource.groovy) you can't even set their values in the external config - or at least, that has been my experience.

Sunday, May 5, 2013

Switching from OpenELEC to XBMC + Raspbian on Raspberry Pi

I decided to switch from OpenELEC to XBMC installed on Raspbian due to all the issues I've experienced with the latest releases - random lockups and unreliable wireless network connection make it completely unusable, and OpenELEC's stripped-down nature makes it difficult to diagnose and implement workarounds. From what I read on the OpenELEC forums these instability issues are due to old firmware in the build, but since all my Raspberry Pi hardware already runs fine on the latest Raspbian I decided to just install XBMC on top of that.

Apparently there are other issues with running XBMC on Raspbian, such as the full keyboard not working in the XBMC interface, but that can be worked around. With a stable wifi connection I can edit config files via ssh. Also, once XBMC is configured to enable remote control via http the soft keyboard on the smartphone XBMC remote app will work.

Since I'm using BerryBoot, I used the "clone" function to create a clean copy of the latest Raspbian image. With the latest BerryBoot I was able to do this via VNC, and as an unexpected bonus my wifi config settings got carried over into the new Raspbian install. I named it "Raspbian XBMC," set it as default, then booted into it and went through the usual setup steps in "raspi-config." Before running updates I install screen so that I can let the long update run and just disconnect from the ssh session. After that I did the usual "sudo apt-get update" and "sudo apt-get upgrade" to get everything up to date.

I installed XBMC as per Michael Gorven's instructions here and edited /etc/default/xbmc so XBMC would run on startup as user pi. After that it's more or less just installing plugins and customizing.

Sunday, April 14, 2013

Fixing Samba master browser conflicts between multiple OpenElec instances

The default OpenELEC Samba configuration (in the 1.x and 2.x versions I ran) appears to set itself up as the local network "master browser," which is responsible for managing the list of network shares and so on. This makes some sense, since the computer with shared media files is likely to be left on all the time. 

But if you have more than one OpenELEC instance running (or have set up Samba with borrowed settings from the OpenELEC config), it's possible for the wrong computer to become the master browser, and when it's shut down or rebooted all of the network shares suddenly become un-browsable. This was happening when I would temporarily boot one Raspberry Pi to OpenELEC, watch or listen to stuff shared from the "main" HTPC for a few hours, then reboot it back into Raspbian.

To resolve this I changed the Samba configs as below; I believe the wins support settings are needed for name resolution in Network Neighborhood in Windows:

On the primary instance (the one that will always be running):
  - domain master = yes
  - local master = yes
  - preferred master = yes
  - os level = 100
  - wins support = yes
  - name resolve order = lmhosts wins bcast host


On the other instances (the ones not left on all the time):
  - preferred master = no
  - domain master = no
  - local master = no
  - os level = 5
  - wins server = <ip address of the primary instance>
  - name resolve order = lmhosts wins bcast host

Check which computer is currently the master browser:
smbclient -L <ip address>

http://openelec.tv/forum/12-guides-tips-and-tricks/7791-having-problems-browsing-samba-shares-readme


Tuesday, February 26, 2013

Keeping a smartphone from dropping the wi-fi connection

This is a "note to self" post covering what I've tried to keep my phone (a Motorola Atrix MB860) from repeatedly dropping and reconnecting to my wireless network.

* Connecting to Wireless G seems more stable than N (probably varies with the router) - on my dual-band router I have the two bands set up as Wireless G and Wireless N networks with different SSIDs for identification.

* Change router DHCP lease time changed from "forever" to the next longest time available ("two weeks" in my case) - I recall reading that "forever" is actually sent as a really large numeric value that is not properly handled by some devices - it instead overflows into a really small value.

* On the phone:
* When you want to stay connected, set Battery Mode to Performance. Depending on the application (such as Netflix or Hulu Plus) you will also want to keep the display from sleeping (set Display Timeout to Never).
* Additional settings I set:
* In Wi-Fi Settings uncheck the options to auto connect to "AT&T Wi-Fi Hot Spot" - I think this reduces the phone scanning for other wi-fi networks and possible switching when you're already connected to your preferred/home network.
* Uncheck Network Notification > Open Network and Secure Network - again, I think this reduces scanning for other networks.
* In Wi-Fi Networks, remove the Wireless N network and set up the Wirless G one to make sure we connect to the preferred one.

So far so good - my phone has not dropped my wi-fi connection while I was using it over the last two days.