Integrating LiFX with Artemis

Introduction

Artemis Spaceship Bridge Simulator is an awesome game in which several people with several computers sit in a room together and take different roles on the bridge of a starship (Captain, Helm Officer, Weapons Officer, etc). The game runs on Windows. It can interface with DMX lighting using USB DMX controllers based on FTDI USB-serial chips, to do such things as turning the lights on the bridge red in a “red alert” situation.

I don’t have any DMX lights or FTDI chips, but I do have a smart light bulb from LIFX which is controllable over Wi-Fi. I’ve made this light bulb work with Artemis, although it’s a bit of a hack.

Details

I used USB/IP, which forwards a USB connection over a network. There’s a USB/IP Windows client, which runs on the same computer as the Artemis server. Usually, the USB/IP client would connect to a USB/IP server and present real USB devices from the server as local devices on the client. But in my case, the client talks to a pair of scripts I wrote which emulate an FTDI-compatible serial device and translate the DMX commands to LIFX commands.

How data flows from Artemis to a DMX light normally:
Diagram shows data flowing from Artemis through Windows to FTDI chip and DMX light

How data flows from Artemis to my LIFX light:
Diagram shows data flowing from Artemis through Windows, USB/IP client, USBIP.py, and lifx-dmx.rb, to LIFX light

My code is available on GitHub. It comprises two scripts. USBIP.py, which is based on some existing USB device emulation code by Yaron Shani, emulates an FTDI USB device and writes base64-encoded DMX packets to standard output. lifx-dmx.rb reads these packets and uses the LIFX gem to send appropriate commands to the light bulb. These two scripts should be run with python USBIP.py | bundle exec ruby lifx-dmx.rb.

The code expects Artemis to be configured with a DMXCommands.xml file generated by my generate_xml.py. LIFX bulbs are a bit “smarter” than DMX lights. For example, to quickly and smoothly fade an LIFX bulb from one colour to another takes a single command, whereas in DMX this would take a constant fast stream of packets updating the colour. So rather than using Artemis’s default configuration which specifies lights and colours directly in DMX, I associate specific events such as RED_ALERT with specific DMX channels, and thus with specific rich LIFX commands.

My understanding of how Artemis’s DMX processing works is as follows. Each time Artemis generates a DMX packet, it starts with the previous state of the DMX channels, then processes the XML file in order and applies each setvalue tag for each active event. Some events (including RED_ALERT) are like level-triggered signals – they have a start and an end, and may have continuous="yes" in the XML file so that Artemis keeps the relevant event active until it finishes. Other events are like edge-triggered signals – they happen in a single moment, and should have continuous="no" in the XML file so Artemis will make the event inactive a short time afterwards.

I only have one LIFX bulb and I’ve only implemented turning it red for “red alert”; there’s plenty of scope for adding more and better effects.

My Artemis server runs Windows XP, because I’m running it in VirtualBox and have had trouble getting the graphics working on newer Windows versions. The available compiled USB/IP client usbip.exe is older than the available source code, and doesn’t work on Windows XP. I used Visual Studio C++ 2010 Express to compile a new usbip.exe myself.

Demonstration video