Introduction


As a developer, adding support for the See Munkey device helps solidify it as a new part of the audio games community. I thank you in advance for this, but I believe this will do more than help only me. Each new program or game that supports the device helps encourage others to get one for themself. In turn, as more people who own a See Munkey, more developers are likely to begin supporting it. Once the device has reached the hands of enough players, all audio game developers everywhere will have the freedom to design games relying on this technology, without worrying that it will limit their potential sales. End of rant. :D

Depending on the programming language you're using, adding support for the See Munkey will be different, but here is the main idea that will be the same through all of them. After this section you can find code examples for specific languages.

You will open a serial connection to the COM port the device is plugged in to. Your game can prompt the user for this number and perhaps store it in a file so they don't have to enter it each time they play. I also won't be surprised if people develop some code to automatically detect the correct port so that the user never has to be asked. My auto detection code seems to work great unless someone has a wireless keyboard plugged in at the same time (Yes I'm looking at you Ryan!), haha, but soon after posting this manual I'll probably have that figured out.

Your serial connection will have a command to send data, and a command to read it. There are only 3 commands that will cause the See Munkey to send back a message, they request the version, the processed data, and the raw data from the sensors. Depending on the language you're using, incoming messages from the device might arrive in pieces. For example, instead of receiving (See Munkey build 1.0.3), you might first receive "(See Munk", followed by a second message of "ey build 1.0.3)". I suggest you take incoming messages and combine them in a variable as they arrive. All See Munkey messages end with a closing bracket, so once your variable has one, you know you have the entire message.


Message examples


To have the device send back the current firmware version it is running, you will send the device a lowercase v. After getting your command, the device will send you the version information contained within brackets ( ). The brackets help you always know what kind of information you are receiving, where it begins and ends, and lets you know that you have the entire message. Knowing the firmware version isn't really all that useful, but it is available as a command. :)

You send, v
You receive, (See Munkey build 1.0.3)

The command you are most likely to use, requests the processed positional data. To request this data you will send the device a lowercase p. The return message will be contained within square brackets [ ], where 3 numbers are separated by commas. Even the last number has a comma at the end, because I felt this would make parsing the information easier. Each of the numbers will have 2 decimal places and may be either positive or negative. In this order they are the yaw, pitch, and roll measurements from the device.

Yaw is the person turning their head to face left or right. Pitch is tipping their head up or down to face the ceiling or floor. Roll is tilting their head left or right, such as bringing your left ear down closer to your left shoulder while you continue to face straight ahead. These values are presented in degrees, and none of them will go above 360 or below -360. Because 360 degrees is the same as 0 degrees, the device keeps all numbers cycling within the range of -359.99 to 359.99 in the processed message. If for some reason you do not want the values to cycle, you will want to check out the raw positional data which is next.

You send, p
You receive, [153.24,29.52,-46.56,]

There is a lot more going on within the device than 3 easy to read values, so you also have an option to read the raw positional data. To request this data, send the device a lowercase w. The return message will be contained within curly brackets { }, where 13 numbers are separated by commas. Even the last number has a comma at the end, because I felt this would make parsing the information easier. Each of the numbers will have 2 decimal places, and may be either positive or negative. The first 3 values are the raw sensor data from the accelerometers, the next 3 are from the gyroscopes, then 3 are from the magnetometers, 1 value is the timing clock (which is pretty useless but I have it in here anyway), and then the yaw, pitch, and roll. The yaw, pitch, and roll values are almost the same as if you had requested processed data except these have not been limited to values between -360 and 360.

You send, w
You receive, {-230.50,45.30,-36.20,22.70,6.20,0.80,799.30,101.30,-3415.10,-126.55,153.24,29.35,-46.58,}


Each of the main sensor types has 3 values because there are 3 of each. Each one will be orientated to deal with its own axis, such as X, Y, and Z. Figuring out which of the values you want tends to require a little guess and check, but also keep in mind that many of the values affect one another as the person moves.

The first 2 accelerometer values deal with position, so they are pretty easy to understand, and the third value deals with gravitational force down along the Z axis. If your head were perfectly flat and you jumped, that sensor would spike as G-forces were applied from the jump, and then it would reach zero and go negative as your body peaked and began to free fall.

All 3 gyroscope values deal with force, and not position. This makes them very tricky to use, especially since each one affects the others. If you are standing still, all 3 values will zero-out, or at least they will settle within a range that represents their "at rest" state. Turning will raise or lower the values depending on force around those axis, where the faster you turn represents the largest change. As you slow and come back to a stop, all will once again go back to their resting values.

The magnetometers all deal with position, but they are highly affected by magnetic and electrical fields. In theory these should be sensitive enough to create a compass, or also find power lines within your walls. That sounds like a joke, but my friend Steve and I found we could map the position of power lines in my wall by watching these values as we moved the See Munkey back and forth. I think he said it spiked when his cell phone rang as well, but I haven't verified that. So basically these are quite powerful and have many uses, but they are also unstable because of how many outside forces can be having an effect. If you are using these to sense motion, for example, always check gyroscope and accelerometer data to verify that you are in fact moving.

Even though requesting raw data seems to make the device work much harder, never worry about that. The device can easily calculate and present its data faster than your program can use it.


Calibration commands


We've covered the 3 commands which cause the See Munkey to return data, so now we'll cover the 7 commands that do not. Even if you are not trying to create your own calibration features for the device, you will at least want to let the player center themself during your game/program. The See Munkey is an IMU (Internal Measurement Unit) and all IMU's rely on dead reckoning as their means for calculating position and orientation. While this method has many advantages, the main disadvantage is that it will ocassionally lose its direction as small math errors built up. When this happens it is in need of a "land mark" to let it correct itself and get back on track, which we call centering.

To tell the device to center itself, simple send it a lowercase c. You won't get any messages back. You send, c

If you wish to make your own calibration program, or offer this within your game as a convenience for the player, here are the other 6 commands you will need:

To send the calibrate "left" command, send the device a lowercase l. You send, l

To send the calibrate "right" command, send the device a lowercase r. You send, r

To send the calibrate "up" command, send the device a lowercase u. You send, u

To send the calibrate "down" command, send the device a lowercase d. You send, d

To send the calibrate "tilt left" command, send the device 7. You send, 7

To send the calibrate "tilt right" command, send the device 9. You send, 9

If you have a numberpad on your keyboard, the 7 and 9 might make more sense. Originally I was going to use only numbers for the calibration data, where 5 is center, 8 is up, 2 is down, 4 is left, and 6 is right. In that scenario, 7 and 9 seem to fit as tilting your head left or right. I ended up changing the main calibration commands over to letters because I thought they would be easier to remember. I don't know how many people out there have number pads, especially since most laptops don't even include them anymore.


Example code for different langauges


Visual basic 6 using MS Comm control

Project -> Components -> Then check Microsoft Comm Control 6.0. Now add it to your project from the toolbox.

Private Sub Form_Load()
  With MSComm1
        If .PortOpen Then .PortOpen = False
        .CommPort = 1
        .Settings = "115200,N,8,1"
        .DTREnable = True
        .RTSEnable = True
        .RThreshold = 1
        .SThreshold = 0
        .PortOpen = True
  End With
End sub

MSComm1.Output = "p"

Private Sub MSComm1_OnComm()
dim incomingmessage as string
incomingmessage = MSComm1.Input 'this variable now holds the message the device sent back to your program.
totalmessage = totalmessage & incomingmessage 'we may only have part of the message, so I'll add it to another variable that is being used to store up the total message.
End sub

Java code alongwith RXTX Libraries

(Example pulled from this website: link)
RXTX can be downloaded here: link
The site that covers all of this is: link
I'm still working on this section.


Visual C++

(Example pulled from this website: link)
This website seems to have a serial class already created for you: link

#include "stdafx.h"

using namespace System;
using namespace System::IO::Ports;
int main()
{
	String^ portName;
	portName = "COM1"; // For this example I'm assuming the COM port is 1.  You may wish to ask the user what number to use, and store it for when they play again in the future.
	int baudRate=115200;
	SerialPort^ seemunkey;
	seemunkey = gcnew SerialPort(portName, baudRate);
	// open port
	try
	{
		seemunkey->Open();
	}
	catch (IO::IOException^ e  ) 
	{ 
		Console::WriteLine(e->GetType()->Name+": Port is not ready");
	}
	catch (ArgumentException^ e)
	{
		Console::WriteLine(e->GetType()->Name+": incorrect port name syntax, must start with COM/com");
	}

	// Send message to the device requesting version info.
	seemunkey->WriteLine("v"); // send v to arduino

	// End program.  It's a good idea to close the port when you're exiting your game, though in Windows I don't think you need to worry about it.
	seemunkey->Close();
	Console::Write("Press enter to close the program");
	Console::Read();
    return 0;
}

Special Note for C++: When addressing ports larger than COM9 in Windows you will have to specify the port thusly: "COM10" becomes "\\\\.\\COM10" (See: http://support.microsoft.com/default.aspx?scid=kb;EN-US;q115831 )


C#

(Example pulled from this website: link)

In your project, go to adding a new control and add the SerialPort class. Set your baud rate to 115200 Remember to set your port name, which will begin with COM. The Read buffer size, and write buffer size can both be left as the defaults.

serialPort1.Open();  - Opens the serial port for you to use. There will be a big nasty error if the port is already opened, or if the port is not there.

serialPort1.BytesToRead - use an if statement to compare to 0. If the result is false, then there is serial data available (if(serialPort1.BytesToRead == 0) is the same as for arduino if(Serial.available))

Talking to the device (either write, or writeline should both work)

serialPort1.Write(arg); - Tells the arduino something, where arg is what you want it to say. There will be a big nasty error if the port is not opened.
serialPort1.WriteLine(arg); - same as serialPort1.Write(arg); but always adds "\n".

Reading from the device

string read = serialPort1.ReadTo(arg); - Reads the serial data, until the text in arg is found, then is returned as read. Also will have an error if the port is not opened.
string read = serialPort1.ReadLine(); - Same as serialPort1.ReadTo("\n");
string read  = serialPort1.ReadToEnd(); - Keeps reading until there is no more data to read, then is returned as string read.

Code example to request the device version:

serialPort1.Open();
if (!serialPort1.IsOpen)
{
	try
	{
		serialPort1.Open();
		serialPort1.Write("v");
		serialPort1.Close();
	}
	catch
	{
		MessageBox.Show("There was an error. Please make sure that the correct port was selected and the device is plugged in.");
	}
}


Link to audio games
Link to audio devices
See Munkey Developer's manual provided by Kaldobsky L.L.C.