Saturday, September 1, 2012

Raspberry Pi, MAME, and Ultrastik, pt 1

Among the things I want to do with the Raspberry Pi is to build a miniature arcade thing. I had planned on making one with one of my mini-itx systems, but the Pi just seems so much better. And honestly, it's more stable than any of the mini-itx systems I've used. I had already ordered an Ultimarc Ultrastik 360 and a bunch of arcade buttons. The Ultrastik is awesome, I seriously wouldn't build any type of arcade without using one of these.  One of the things it can do is flash 9x9 control maps so it can be a 4-way, 8-way, 2-way, and more. It also hooks up via usb, supports direct connection of buttons, and the 2.5 firmware can use one of those as a shift (so it basically can support about 16 buttons). Awesome. I got the medium-firm spring and the circular restrictor plate to give it a shorter throw.
I started with the Adafruit Occidentalis build for my RPi, which came with the stuff I needed "out of the box" to get the WiFi dongle working.
Earlier experiments trying to get MAME and a frontend gui working didn't go so well, but I ran across a few posts that helped. I followed Chris's guide and parts of Ben's post to get AdvanceMAME and AdvanceMenu compiled and installed. I'll skip the hassle I had getting all the appropriate files & configurations loaded into the right directories.
Then I snagged Al Crate's configuration utility for linux (link goes to Ultimarc page, code is linked at second line from top). To get that to compile, I needed to grab a few things and compile libhid (because it's not available to RPi via apt-get install yet for some reason). But to get *that* to compile without a warning throwing the whole thing out of the make command, I had to edit the libhid-0.2.16/test/lshid.c
There's a line that says:

len = *((unsigned long*)custom);
Right below it, I threw this:
if(len==0){;}

So that got libhid compiled, and after an ldconfig I got ultrastikcmd to compile. The next set of problems I ran into were:
  • When I ran ultrastikcmd to load a map into the Ultrastik, it dropped off the machine. jstest /dev/input/js0 no longer found it (although an ls /dev/input/ shows a js0 node).
  • When I unplugged and replugged the joystick, the jstest would work again but the input either didn't work at all or was scrambled.  I did a LOT of using my Windows machine to load new maps into joystick.
I ended up fixing the second issue first -- after adding some code to ultrastikcmd.cpp to give it a verbose debugging flag, it appeared that the default map border values (a parameter/setting on the Ultrastik that allows you to tweak the 9x9 grid sizes, I *think* -- but it isn't very well documented yet) were being set by the program but it was passing all 0's to the device. I tweaked that and at least when I unplugged/replugged the device the stick was passing appropriate values to jstest. I'm sending the code to Al so he can get it into the next version, but if you need all the debugging code, etc. let me know.
The section of code I fixed is toward the bottom -- I just commented out the if and closing brace. Since it sets the defaults & overrides them if you load them from a map or command line, it should be ok to write them to the device no matter what. I might not have needed to do this if the Ultrastik configuration maps I used had map values in them.
//if(arguments.border_set){
// Byte[3]-Byte[10] = Map Border Location
memcpy(&data[3],arguments.border,sizeof(arguments.border));
//}

The remaining big issue was the fact that the thing kept falling offline. After hours and hours, and a significant amount of searching, I figured out that when ultrastikcmd takes control of the joystick to flash a map to it, the system unbinds the kernel driver. I tried a couple dozen things to get it rebound programmatically (and learned a lot about devices and usb on linux in the process), but the reattach code isn't in the libusb or other stuff built into the RPi image I was using, and to heck if I'm going to re-compile the whole OS.
I ended up creating a whole lot less elegant solution, but it works.
Here's what we started with. You can see that jstest works - the joystick gives appropriate readings when I move it.
$ jstest /dev/input/js0
Driver version is 2.1.0.
Joystick (Ultimarc Ultra-Stik Ultimarc Ultra-Stik Player 1) has 2 axes (X, Y)
and 16 buttons (Trigger, ThumbBtn, ThumbBtn2, TopBtn, TopBtn2, PinkieBtn, BaseBtn, BaseBtn2, BaseBtn3, BaseBtn4, BaseBtn5, BaseBtn6, BtnDead, BtnA, BtnB, BtnC).
Testing ... (interrupt to exit)
Axes: 0: 0 1: 0 Buttons: 0:off 1:off 2:off 3:off 4:off 5:off 6:off 7:off 8:off 9:off 10:off 11:off 12:off 13:off 14:off 15:off
Now switching to 4-way map
$ ultrastikcmd -vfou ~/ustik_maps/4-way.um
And trying jstest again -- no love
$ jstest /dev/input/js0 jstest: No such device

One of the things I learned about along the way is sysfs. That and a bit of poking around yielded something interesting. This presumes you have usbip installed, and gives you a list of usb devices by USB BUS ID. d209:0501 is the Ultrastik.
$ sudo usbip list -l
- busid 1-1.3.3 (d209:0501)
        1-1.3.3:1.0 -> unknown
        1-1.3.3:1.1 -> usbhid
See that little "unknown" there? That's the not-bound kernel driver. Here are the input devices -- notice there aren't any js0 entries, but there is for event1 (1-1.3.3:1.1), which comes from the Ultrastik. Note: for this and other directory listings, I'm going to edit them down to save space & improve readability.
$ ls -la /sys/class/input/
event1 -> ../../devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.3/1-1.3.3:1.1/input/input10/event1
mice -> ../../devices/virtual/input/mice
mouse0 -> ../../devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.3/1-1.3.3:1.1/input/input10/mouse0
It doesn't show above as bound to the driver, but we can double check. No driver listed!
$ ls -la /sys/bus/usb/devices/1-1.3.3:1.0/
bAlternateSetting
bInterfaceClass
bInterfaceNumber
bInterfaceProtocol
bInterfaceSubClass
bNumEndpoints
ep_82
modalias
power
subsystem -> ../../../../../../../../bus/usb
supports_autosuspend
uevent

This one is (the one for event1, which is the other BUS ID listed by the usbid command).
$ ls -la /sys/bus/usb/devices/1-1.3.3:1.1/
0003:D209:0501.000B
bAlternateSetting
bInterfaceClass
bInterfaceNumber
bInterfaceProtocol
bInterfaceSubClass
bNumEndpoints
driver -> ../../../../../../../../bus/usb/drivers/usbhid
ep_81
input
modalias
power
subsystem -> ../../../../../../../../bus/usb
supports_autosuspend
uevent
Yup, a driver!

Now for the fun part. Evidently, if you write the appropriate usb bus ID to the kernel driver bind file, it rebinds. Hmmm...
echo -n "1-1.3.3:1.0" | sudo tee -a /sys/bus/usb/drivers/usbhid/bind
You need to put the sudo in the right spot and use tee because the user doesn't have write permissions to bind. I'll fix that later.

Now lets check to see if it appears rebound:
$ sudo usbip list -l
- busid 1-1.3.3 (d209:0501)
        1-1.3.3:1.0 -> usbhid
        1-1.3.3:1.1 -> usbhid

Yes! And importantly, jstest worked again!

Now how do I keep from having to manually do this every time I load a map into the Ultrastik.  Enter udev (and two more links) and some shell scripting.
First step, since I want to try and programmatically be able to rebind in user (vs. root) space, I'm changing permissions/group for the usbhid kernel bind. I'm sure someone will point out all kinds of evil wrong things this will do, but this isn't a server/production machine... it's an arcade joystick.
$ sudo chmod g+w /sys/bus/usb/drivers/usbhid/bind
$ sudo chown root:pi /sys/bus/usb/drivers/usbhid/bind
$ ls -la /sys/bus/usb/drivers/usbhid/
--w--w----  1 root pi   4096 Aug 31 23:45 bind

To make things easy, I also want to create a symlink to the bind in a convenient location.
$ ln -s /sys/bus/usb/drivers/usbhid/bind ~/ultrastik/usbhid_bind

Now I need a udev rule. I've added a rule to write the kernel to a file under the home folder, for ease of access in my shell script.
$ cat /etc/udev/rules.d/81-ultrastik.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="d209", ATTRS{idProduct}=="0501", GROUP="input", RUN+="/home/pi/receive_udev.sh '%s{manufacturer}' '%k'"


That matches the Ultimarc Ultrastik, ensures when it's added that a group the user is in can get to it, and then runs a custom shell script passing in a couple variables. I'm not using the first one (manufacturer), it was just for testing. Here's the script that receives that rule and writes the usb bus id out. I'm hard-coding the ":1.0" part in hopes that doesn't screw it up later.
$ cat receive_udev.sh
#!/bin/bash
echo "$2:1.0" > /home/pi/ultrastik/ultrastik_sysfs_id

So now all I need to do to fix/rebind the joystick is:
$ cat ~/ultrastik/ultrastik_sysfs_id > ~/ultrastik/usbhid_bind

I've tweaked the AdvanceMenu configuration (~/.advance/advmenu.rc) emulator line to call a generic emulator (my script):
emulator "AdvanceMAMECustom" generic "/home/pi/ultrastik/advshim.sh" "%s"

That script is as follows -- where you see rom1|rom2, etc. you would replace those with the names of the roms that need a specific Ultrastik map.
#!/bin/bash
# if no command line arg given
# set ROM to Unknown
if [ -z $1 ]
then
  ROM="*** Unknown ROM ***"
elif [ -n $1 ]
then
# otherwise make first arg as a ROM
  ROM="$1"
fi
case $ROM in
        # 8-way
        rom1|rom2|rom3)
          ultrastikcmd -ou ~/ultrastik/ustik_maps/8-way.um
          cat ~/ultrastik/ultrastik_sysfs_id > ~/ultrastik/usbhid_bind
          ;;
        # 4-way
         rom4|rom5)
          ultrastikcmd -ou ~/ultrastik/ustik_maps/4-way.um
          cat ~/ultrastik/ultrastik_sysfs_id > ~/ultrastik/usbhid_bind
          ;;
        *)
          echo "Sorry, no ROM defined!"
          ;;
esac
/usr/local/bin/advmame $ROM

So...

  1. On boot or Ultrastik insert, udev catches it and writes something like 1-1.3.3:1.0 to  ~/ultrastik/ultrastik_sysfs_id
  2. When you launch a game from advmenu, it calls the  advshim.sh script, passing in the rom name.
  3. The script checks the rom name to find the appropriate ultrastik configuration map, 
  4. ...runs the ultrastikcmd configurator (losing the joystick in the process), 
  5. ...writes the contents of the previously-written usb bus ID to the bind to pick back up the joystick,
  6. ...then launches advmame with the rom.
Easy, right?  I really did want to do something elegant in the ultrastikcmd program itself, like issue a USBDEVFS_CONNECT command via ioctl. But that didn't work (kept giving me a device busy error). If someone out there has suggestions on how I can better accomplish this, I'm all ears.
Now all I need to do is wire up the buttons, get them all configured properly, and build a case for the whole assembly.
And PS -- don't load a strict 2-way only map... you can't navigate advancemame without up & down. I'll make a map that had most of the 9x9 grid filled with left and right, and only a narrow up/down portion for anything that might require only left and right normally.

Sunday, August 5, 2012

Washington Cities

Both my wife and I like the look of those list-of-city-name signs/posters/artwork that are cropping up everywhere. And since we are planning to rework the theme of our son's room to more of an exploration and travel motif, thought one would work well in there. I thought one with a bunch of city names from the state we live in (Washington) would be fun, so I hammered this out today. I'm doing a small size test print at Costco for a color check, then we'll probably do a 20x30 & mount/frame it somehow.

Tuesday, July 31, 2012

Filled Cylinders for Dinner

For some crazy reason "filled cylinders" as a theme came to mind when I decided to cook something yummy and not-quite-normal for dinner. It turned out to be an extremely awesome combination of flavors, and actually pretty healthy. It consisted of:
A red pepper section (fresh raw) deseeded/devained & filled with spanish rice (box -- I made this after a full day of work & taking Hunter to TKD... no time for scratch). Garnished with thin slices of Cougar Smoky Cheddar.
A roma tomato half (fresh raw) scooped & filled with cottage cheese & a garnish of romaine.
Three carrot sections (fresh raw), cored and filled with a puree of the leftover carrot, neufchâtel cheese, 1 Tbs soy sauce, 1 tsp turmeric, 1/2 tsp chili powder.
Garnished with (fresh raw) avocado slices and more carrot puree.
A person *could* cook/bake the tomato, pepper, and carrots... but they'd just not taste the same.
Dessert is another filled cylinder - strawberry shortcake with whip cream!

Thursday, July 19, 2012

Raspberry Pi and autostart

I've had a heck of a time figuring out how to get a program (feh, in this instance) to autostart after my Raspberry Pi auto launches into LXDE. I did finally get it working - my only excuse is I must be tired...
Not that it matters, I'm using the new raspbian distro (downloaded today, 07/19/2012), completely udpated/upgrade (sudo apt-get update and sudo apt-get -y upgrade)
I used the configuration utility to easily tell it to autostart into the window environment (sudo raspi-config) then find the option for booting directly with startx & enabled it.
Then I wrote a script to launch feh (I'll do a more comprehensive post on this later, likely), left it in my home directory and named it start_feh.sh (imaginative), with appropriate chmod +x
Next (and I assume you're doing this all from a terminal/ssh), change to your home folder's  configuration folder (cd ~/.config), and make an autostart directory (mkdir autostart).
cd to that folder (cd autostart), then make a *.desktop file for your script (vi feh.desktop) with the following 3 lines:

[Desktop Entry]
Type=Application
Exec=/home/pi/start_feh.sh

That's it -- all it took. So... long story short: On your Raspberry Pi, to start a program after it launches into LXDE, make a *.desktop file in /home/[user profile]/.config/autostart



Wednesday, April 4, 2012

Communication Bandwidth and Effectiveness

Communication Bandwidth and Effectiveness


I've had some thoughts swirling around my head for a while now -- it's time to get them out.

It is said that 90% of communication is non-verbal in nature.

Let us express the entirety of the message that someone wishes to convey using an arbitrary measurement, such as "units." The higher the communication bandwidth or freqency response, the more message can be communicated at any one time. A purely text- or voice-based communication vehicle (email, instant message, phone call) is unable to convey a message as well as a multi-channel or broadband mechanism such as VTC or face-to-face. In text-based communications, in-band mechansisms to improve encoding the parts of the message that would traditionally be conveyed via alternate channels have grown (versus being explicitly or purposely developed) out of necessity -- emoticons, acronymns (e.g. LOL).
If 100 "units of message" can be conveyed face-to-face in 1 minute, lower bandwidth mechanisms such as email would therefore take more time and effort to convey the same 100 units.  This lack of efficiency and increased work also provides foothold for the introduction of error, resulting in an decreased signal to noise ratio [edited: lower signal, more noise].  Errors in these mechanisms can be considered a miscommunication, which often result in subsequent additional work to correct and realign efforts that were either not started or progressing in a different direction than intended. Over time, habits have been adopted in an attempt to add redundancy to these systems, such as following up an important email with a phone call.
Video teleconferencing with HD video and HD voice is the closest commercially available mechanism to face-to-face high-message-bandwidth communication.  Widespread adoption of HD VTC down to the individual employee level should yield more efficient communications and less rework, and make for fewer face-to-face meetings.  Fewer meetings means less travel time and expense, and a reduced need for conference rooms.  It also improves the cultural acceptance and environment for telework, which again drives down travel expenses and reduces the need for office space.
The implementation of HD video telecommunications to the desktop will be the catalyst responsible for driving more efficiency and cost-savings in the workplace and across our organizations than any other 'single' change.

[edits/additional thoughts]
By "face-to-face" above, I mean in-person meetings. Maybe when my son is my age, "face-to-face" will connotate a physical meeting.