Posts Tagged ‘ commandPort

Arduino to Maya Communication via Python

Arduino + light sensor talking to a sphere in Maya

I’ve wanted for some time to get my Arduino to talk with my 3d application of choice, Maya.  There were a few hurdles to overcome first:

  1. The Arduino communicates to the computer over the serial port (easily, by default).
  2. Maya (to my knowledge), has no built-in serial communication.  If you can find a built in mel command or API call, let me know 😉
  3. Maya comes with its own version of the Python scripting language (2.6ish)
  4. Python (external to Maya) has it’s own (separately installed)  pySerial module.  BUT:
    1. pySerial is a 32-bit app  (no 64-bit build I can find, and I’m not smart enough to recompile it to 64-bit).
    2. I’m running a 64-bit version of Maya, with a 64-bit version of Python = I can’t use pySerial in Maya :-(

Maya can however receive incoming communication over a commandPort, which you can ‘pretend’ is a serial port.  Below I’ll describe how to do that.  A bunch of these steps I’d already discussed in previous blog posts here or on my mel wiki, which I’ll list individually first:

Overview of the process:

  1. Author Arduino code to read sensor data and send it over the serial port.
  2. Author Python code in Maya (Maya’s version of 64-bit Python 2.6) to setup a commandPort, and to do ‘something’ with the incoming data.
  3. Author Python code external to Maya (in 32-bit Python 2.6) to receive the Arduino serial data (using pySerial), and broadcast it to the open Maya commandPort.

Notes about the commandPort code below:

  • I’ve hard-coded the commandPort data below to the address of  “127.0.0.1:7777”.
  • 127.0.0.1 is the localHost address of your machine, you shouldn’t change that.  But the “7777” is an arbitrary port number I made up, feel free to change it.
  • The important thing is that it must be consistent in all the places referenced in the code below.

Step 1:  Author Arduino Code:

This is a very simple sketch showing you the bare-bones requirements. In my case, I’ve hooked up an analog sensor to pin 5 (in my case, it’s a light sensor).
Upload this to the Arduino and start broadcasting!  You can open the Arduino Serial Monitor to see what it’s up to, but be sure to close the monitor before you do any other work.

/**
SerialSend01
Eric Pavey 2011-01-26

Sketch will print the value from the sensor to the serial port.
Sensor is plugged into analog pin 5
*/

#define SENSOR 5
int val = 0;

void setup(){
  Serial.begin(9600);
}

void loop(){
  val = analogRead(SENSOR);
  // Print to the serial port:
  Serial.println(val);
  delay(100);
}

Step 2: Author Python code in Maya:

The below code could eventually be made into a well-packaged Maya-Python module, but for the time being you can just paste it into the Script Editor for execution.  Thing to note:

  • The Maya commandPort command has a ‘prefix’ parameter that takes the name of a mel procedure (not Python function) that can intercept the incoming data.  Because of this, we use Python to create a simple wrapper mel procedure that in turn calls to the Python function that does the work.
  • In the below example, I have the Python function query for the existence of a ‘pSphere1’ object in Maya, and if it finds it, will scale it based on the passed in Arduino sensor values.  This should obviously be changed to something way more cool for what you’re doing :-)
# Eric Pavey - 2011-01-26
# In Maya, via Python:
import maya.cmds as mc
import maya.mel as mm

# Our mel global proc.
melproc = """
global proc portData(string $arg){
    python(("portData(\\"" + $arg + "\\")"));
}
"""
mm.eval(melproc)

# Our Python function that can be changed to do whatever we want:
def portData(arg):
    """
    Read the 'serial' data passed in from the commandPort
    """
    print "Recieved!: ", arg

    # Some silly example code to scale a sphere:
    mappedVal = (float(arg)/1023.0) * 10
    if mc.objExists('pSphere1'):
        mc.setAttr('pSphere1.scale', mappedVal, mappedVal, mappedVal)

# Open the commandPort.  The 'prefix' argument string is calling to the defined
# mel script above (which then calls to our Python function of the same name):
mc.commandPort(name="127.0.0.1:7777", echoOutput=False, noreturn=False,
               prefix="portData", returnNumCommands=True)
mc.commandPort(name=":7777", echoOutput=False, noreturn=False,
               prefix="portData", returnNumCommands=True)

Step 3: Author Python module to receive Arduino serial data and broadcast to Maya:

You must make sure you’ve previously downloaded and installed pySerial to a place where your system-installed Python can see it.  And it goes without saying you also need to have Python 2.6 installed as well.

This code will intercept the Arduino serial data, and send it to Maya over the commandPort.  It will only send new data, meaning, it will only send data that has changed from the previous data received.

# arduinoRead.py
# Python module external to Maya
# Eric Pavey - 2011-01-26

import socket
import serial

ARDUINO =  "COM3"

def main():
    maya = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    maya.connect(("127.0.0.1", 7777))
    ser =  serial.Serial(ARDUINO, timeout=1)
    prevVal = None
    while 1:
        # Read the serial value
        ser.flushInput()
        serialValue = ser.readline().strip()
        # Catch any bad serial data:
        try:
            if serialValue != prevVal:
                # Print the value if it differs from the prevVal:
                maya.send(str(serialValue))
                prevVal = serialValue
        except ValueError:
            pass

if __name__ == '__main__':
    main()

Save the above code as a Python module, and then execute it from a shell:

c:\pythonStuff\python arduinoRead.py

This should form the link between the broadcasting Arduino, and the listening Maya session, converting from serial data to data sent over Maya’s commandPort.

Result:

In Maya, make a sphere, and rename it to ‘pSphere1’ if it’s not already.  If all the code is working, you should see the script editor print the received Arduino data whenever it changes, and you should see the scale of your sphere be effected by that data.  Magic!