Friday, 20 August 2010

Rainbowduino + Microsoft Office Communicator

The Project

Create a Cube Grenade with a rainbowduino I had lying about. Cube grenades can be described as:

This, I believe, is where my cartoons work the best- “Cube Grenades”- small objects that you “throw” in there in order to cause some damage- to start a conversation, to spread an idea etc

http://gapingvoid.com/2009/05/13/cube-grenades/

Since I work at a small company (around 60 employees) I could use the 8*8 grid to display peoples communicator status.

The Parts

A rainbowduino is an LED driver platform which can control an RGB matrix. It looks like this:

rainbowduino_LRG

Since it’s basically an arduino you can replace the default firmware with a custom one and make it do what you want.

The next item is the RGB matrix itself:

IMG_2850

This slots into the rainbowduino perfectly

Finally I needed some way to flash/communicate with the rainbowduino. Effectively you need a USB to serial convertor.

I decided upon a seeedstudio UartSB to make it as easy as possible (this also allows you to create a wireless connection via Xbee should you want to!)

uartsb_LRG

The pins also line up perfectly with the rainbowduino so it’s ideal for this project.

I also bought a harness because it came with a  2.1mm DC jack to pin plug adapter (which is what the rainbowduino needs for to power it), and I paid extra for an arduino power supply when I bough my arduino.

hns1

I got most of my parts from www.skpang.co.uk and at the time (March 09) they cost:

Total: £71.54

Ok, so it’s an expensive toy, but I have used them for other projects (including writing minesweeper for the rainbowduino!)

 

Please note that you can use an arduino to flash and communicate with the rainbowduino instead of the UartSB as shown in this image:

arduino_rainbowduino

http://www.rngtng.com/2009/06/25/rainbowduino-here-it-is-and-how-to-program-it/

The Idea

I had a play about with getting OCS statuses and decided that the easiest way would be to use COM to connected to a local Communicator client, get all the statuses and send them via the usb serial port to the rainbowduino.

I could have saved it to a database and used an ethernet shield to get the data, but I decided since a machine would have to be on to get the statuses anyway I might as well just use the easiest method.

The Rainbowduino Program

The first thing I had to get working was the rainbowduino. The firmware it comes with is pretty good, but you have to use I2C to send it instructions and the firmware doesn’t allow you to specify an individual pixels colour.

The firmware was a really good starting place for me, but I hade to modify it quite a bit. By default the rainbowduino starts with a pretty screen, but I wanted it to start with every light turned off. This can be achieved by modifying the multi dimensional array in the data.c file.  The array is defined as follows:

unsigned char dots_color[5][3][8][4] = {…}

This array allows for a buffer (up to 5 different images can be buffered) the three colours, and the individual data for each row/column.

so to set the first two pixels to green you would set the following:

dots_color[Buffprt][0][0][0] = 0xff

A pointer to the screen currently being displayed is stored in the variable Buffprt. the next 0 refers to green, (1=red, 2=blue). The next 0 refers to the first row, and the final 0 refers to the first two LEDS on the first row.

The column gets complicated because it uses a byte (8 bits) to define two LEDs. This is a really clever way of saving memory, but it does get complicated!

To turn the first LED you would use the bits 00001111 (0Xf) and for the second LED you would use 11110000 (0xf0).

Once I understood this I could write a setpixel function with the following signature:

void SetPixel(byte red, byte green, byte blue,  unsigned char rowToSet, unsigned char columnToSet)

I liked the idea of the buffer, so the first thing I do is copy the current buffer into the next frame – this means the rainbowduino will never forget current statues, and will only modify the led we want to change. It’s important to note that this really isn’t required and you could just modify the current frame.

  unsigned char color=0,row=0,dots=0;

for(color=0;color<3;color++)
{
for (row=0;row<8;row++)
{
for (dots=0;dots<4;dots++)
{
dots_color[((Buffprt+1)&1)][color][row][dots] = dots_color[((Buffprt))][color][row][dots];
}
}
}


The next thing I had to do was find out which column we are actually setting – for example if the row we are trying to set is the last LED on a row we would pass in 7 (LEDs are 0 indexed). This would translate to column 3, and the last 4 bits on this byte.


Lets say the 6’th and 7th LED were currently 1/2 on, and we wanted to turn the 7th LED fully on. this would mean that the byte was currently:


00110011


What we need to do is create a mask that will blank out the first four bits, and then or it with the value we want to set.


So in our example the mask would be:


00001111


We would and the mask with the current value leaving:


00110011


&


00001111


=


00000011


We then need to add in the new value of the LED. We do this by first bit shifting our input (0xf) and then or’ing it with the above value.


e.g.


1111 << 4 (0xf bit shited 4 places)


=


11110000


We then or this with the original byte value after it has been and’ed with the mask.


00000011


|


11110000


=


11110011


This is exactly the result we want. This is all achieved with the following code:


 boolean firstBit = columnToSet % 2==0;
columnToSet = columnToSet/2;

Value = red;
Mask = 0xf0;
if(firstBit)
{
Value=red<<4;
Mask=0x0f;
}

color=1;
Value = (dots_color[Buffprt][color][rowToSet][columnToSet] & Mask) | Value;
dots_color[((Buffprt+1)&1)][color][rowToSet][columnToSet] = Value;


This is repeated for the other two colours.


Finally all we have to do is tell the current buffer to move on by one.


Now that we have written the setpixels method we need to write some way to communicate with our rainbowduino via the serial port.


This is done via the following code:


void loop()
{
unsigned char color=0,row=0,dots=0;

if (Serial.available() > 0) {
// read the incoming byte:
byte red = Serial.read();
delay(2);
byte blue = Serial.read();
delay(2);
byte green = Serial.read();
delay(2);
int row = Serial.read();
delay(2);
int column = Serial.read();

// say what you got:
Serial.print("I received: ");
Serial.print(red);
Serial.print(",");
Serial.print(blue);
Serial.print(",");
Serial.print(green);
Serial.print(",");
Serial.print(row);
Serial.print(",");
Serial.println(column);
SetPixel(red, blue, green, row, column);
Serial.println("sent");
}
}

There is a delay between reading the value in the Serial port to ensure the next value is in the buffer. But the code is pretty simple. It listens for 5 bytes of data then pushes the result into the SetPixel method we created before. As long as the data doesn’t get send to quickly it works fine, but this is pretty nasty code and I’m sure there are better ways of doing this.


The Office Communicator Software


The integration with OCS is quite easy. By downloading the SDK then adding a reference to CommunicatorAPI you can get all the statuses you need.The code below attaches allt eh events we will require, logs in, then does the initial load of the contacts



CommunicatorAPI.MessengerClass _messenger;
public OCSInterface()
{
_messenger= new CommunicatorAPI.MessengerClass();
_messenger.OnContactStatusChange += new DMessengerEvents_OnContactStatusChangeEventHandler(_messenger_OnContactStatusChange);
_messenger.OnIMWindowContactAdded += new DMessengerEvents_OnIMWindowContactAddedEventHandler(_messenger_OnIMWindowContactAdded);
_messenger.OnIMWindowContactRemoved += new DMessengerEvents_OnIMWindowContactRemovedEventHandler(_messenger_OnIMWindowContactRemoved);
if (_messenger.MyStatus != MISTATUS.MISTATUS_ONLINE)
{
_messenger.AutoSignin();
}
LoadContacts();
}


Because we don’t really want to be dealing with iMessengerContacts in other bits of code I translate the objects into a Plain Old CLR Object defined below:



   public class Contact
{
public string FriendlyName { get; set; }
public string SipAddress { get; set; }
public Status Status { get; set; }
public int Row { get; set; }
public int Column { get; set; }
}
public enum Status
{
Offline =0,
Online=1,
Busy=2,
Away=3,
Idle,
Phone
}


This shows the different status I’m going to be using, and also shows the pixel the contact relates to.


The load contact method does this translation and is shown below:


private Dictionary<string, Contact> _contacts = new Dictionary<string, Contact>();
private void LoadContacts()
{
foreach (CommunicatorAPI.IMessengerContact messengerContact in (_messenger.MyContacts as CommunicatorAPI.IMessengerContacts))
{
Contact contact = new Contact();
contact.FriendlyName = messengerContact.FriendlyName;
contact.Status = Status.Busy;
switch (messengerContact.Status)
{
case MISTATUS.MISTATUS_OFFLINE:
contact.Status = Status.Offline;
break;
case MISTATUS.MISTATUS_ONLINE:
contact.Status = Status.Online;
break;

case MISTATUS.MISTATUS_AWAY:
contact.Status = Status.Away;
break;
case MISTATUS.MISTATUS_IDLE:
contact.Status = Status.Idle;
break;
default:
contact.Status = Status.Busy;
break;
}
contact.SipAddress = messengerContact.SigninName;
_contacts.Add(contact.SipAddress, contact);
}
}

As you can see the pixel location is not set here, this is done when we actually interface with the rainbowduino.


The most import event we attached to on the MessengerClass was the contactstatuschange event. This lets us know when we need to update the rainbowduino. The code simply identifies the Contact POCO object, and updates the status.


In a great example of shocking code I have duplicated the mapping between my POCO status and the messenger status – this should have been a private method – but this isn’t production code and I can’t be bothered to change it! At the end of the call we notify an event that the status has changed, and we should notify the rainbowduino.


public delegate void ContactStatusChanged(Contact changedContact);
public event ContactStatusChanged onStatusChanged;
private void _messenger_OnContactStatusChange(object pMContact, MISTATUS mStatus)
{
Contact contact= _contacts[((CommunicatorAPI.IMessengerContact)pMContact).SigninName];
switch (mStatus)
{
case MISTATUS.MISTATUS_OFFLINE:
contact.Status = Status.Offline;
break;
case MISTATUS.MISTATUS_ONLINE:
contact.Status = Status.Online;
break;

case MISTATUS.MISTATUS_AWAY:
contact.Status = Status.Away;
break;
case MISTATUS.MISTATUS_IDLE:
case MISTATUS.MISTATUS_OUT_OF_OFFICE:
contact.Status = Status.Idle;
break;
case MISTATUS.MISTATUS_ON_THE_PHONE:
contact.Status= Status.Phone;
break;
default:
contact.Status = Status.Busy;
break;
}
if (onStatusChanged != null)
{
onStatusChanged(contact);
}
}


The other two events we attach to respond to the event “IMWindowContactAdded” – this basically means starting and IM chat with someone. I added this as I wanted a way to tell who related to which pixel and this was the easiest way of adding that.



void _messenger_OnIMWindowContactRemoved(object pContact, object pIMWindow)
{
Contact contact = _contacts[((CommunicatorAPI.IMessengerContact)pContact).SigninName];

_messenger_OnContactStatusChange(pContact, ((CommunicatorAPI.IMessengerContact)pContact).Status);
}

void _messenger_OnIMWindowContactAdded(object pContact, object pIMWindow)
{
_messenger_OnContactStatusChange(pContact,MISTATUS.MISTATUS_ON_THE_PHONE);

}


The class I use to interface with the rainbowduino simply opens a serial connection on a port, and sends the required colour and pixel location to the rainbowduino. Notice I have abstracted the colours from rgb to system.drawing.color – this just makes it a bit easier to call the code, however I haven’t implemented every colour, so if a color gets send which isn’t implement it gets translated into Black.


public class RainbowInterface :IDisposable
{
string _portName = "";
SerialPort port;// = new SerialPort(_portName);

public RainbowInterface(string portName)
{
port = new SerialPort(portName);

_portName = portName;
port.BaudRate = 9600;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);

port.Open();
}
public void SetPixel(System.Drawing.Color color, int row, int column)
{
SetPixel(color, row, column, true);
}

public void SetPixel(System.Drawing.Color color, int row, int column, bool showHighlight)
{

byte bRed = 0x0;
byte bBlue = 0x0;
byte bGreen =0x0;
if (color ==System.Drawing.Color.Blue)
{
bBlue = 0xf;
}
if (color == System.Drawing.Color.Red)
{
bRed = 0xf;
}
if (color == System.Drawing.Color.Green)
{
bGreen = 0xf;
}
if (color == System.Drawing.Color.Yellow)
{
bGreen = 0xf;
bRed = 0xe;

}
if (color == System.Drawing.Color.Purple)
{
bRed = 0xf;
bBlue = 0xf;
}
if (color == System.Drawing.Color.Aqua)
{
bGreen = 0x1;
bBlue = 0x1;
}
if (color == System.Drawing.Color.LightBlue)
{
bBlue = 0x8;
}
if (color == System.Drawing.Color.Salmon)
{
bRed = 0x1;
}
if (color == System.Drawing.Color.LightGreen)
{
bGreen = 0x8;
}
if (color == System.Drawing.Color.Orange)
{
bGreen = 0x2;
bRed = 0xe;

}
if (color == System.Drawing.Color.White)
{
bGreen = 0xf;
bRed = 0xf;
bBlue = 0xf;


}
if (showHighlight)
{
byte blueFade = 0xF;
while (blueFade > 0 && blueFade <= 0xF)
{
byte[] bytesBlue = { 0x0, 0x0, blueFade, System.BitConverter.GetBytes(row)[0], System.BitConverter.GetBytes(column)[0] };

port.Write(bytesBlue, 0, bytesBlue.Length);

System.Threading.Thread.Sleep(40);
blueFade -= 2;
}
}
byte[] bytes = { bRed, bGreen, bBlue, System.BitConverter.GetBytes(row)[0], System.BitConverter.GetBytes(column)[0] };
port.Write(bytes, 0, bytes.Length);
System.Threading.Thread.Sleep(50);
// port.Write(sRed+sGreen+sBlue + row + column);
}

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine(port.ReadLine());
}

#region IDisposable Members

public void Dispose()
{
port.Close();

}

#endregion
}

Notice I have also added a highlight animation. This just helps show which pixel has been updated.


The last bit of code is what ties everything together.

class Program
{
private static PresencePoller.RainbowInterface _rainbowInterface;
private static PresencePoller.OCSInterface _cinterface;// = new PresencePoller.OCSInterface();
static void Main(string[] args)
{
_rainbowInterface = new PresencePoller.RainbowInterface(args[0]);
_cinterface= new PresencePoller.OCSInterface();

_cinterface.onStatusChanged += new PresencePoller.OCSInterface.ContactStatusChanged(cinterface_onStatusChanged);
IEnumerable<PresencePoller.Contact> contacts = _cinterface.GetContacts();

var contactsOrdered = contacts.OrderBy(p => p.FriendlyName);

int row = 0;
int column = 0;
foreach (var contact in contactsOrdered)
{
contact.Row = row;
contact.Column = column;

SetStatus(contact);

column++;
if (column == 8)
{
column = 0;
row++;
}
Console.WriteLine((int)contact.Status);
}
while (row < 8)
{
_rainbowInterface.SetPixel(System.Drawing.Color.Aqua, row, column);
column++;
if (column == 8)
{
column = 0;
row++;
}
}
Console.ReadLine();

}

static void cinterface_onStatusChanged(PresencePoller.Contact changedContact)
{
SetStatus(changedContact);
}
private static void SetStatus(PresencePoller.Contact contact)
{
switch (contact.Status)
{
case PresencePoller.Status.Away:
_rainbowInterface.SetPixel(System.Drawing.Color.Orange, contact.Row, contact.Column);
break;
case PresencePoller.Status.Busy:
_rainbowInterface.SetPixel(System.Drawing.Color.Red, contact.Row, contact.Column);
break;
case PresencePoller.Status.Offline:
_rainbowInterface.SetPixel(System.Drawing.Color.Salmon, contact.Row, contact.Column);
break;
case PresencePoller.Status.Idle:

_rainbowInterface.SetPixel(System.Drawing.Color.Yellow, contact.Row, contact.Column);
break;
case PresencePoller.Status.Phone:
_rainbowInterface.SetPixel(System.Drawing.Color.White, contact.Row, contact.Column);
break;
case PresencePoller.Status.Online:
_rainbowInterface.SetPixel(System.Drawing.Color.Green, contact.Row, contact.Column);
break;
}
}
}

All this code does is on load loop through every POCO contact, assign a pixel to the user, then notify the rainbowduino of its status. Then anytime the status changes we update the rainbowduino again.


The final Result


IMG_0062


Please note the paper is used to make the LED colours a bit easier to see

Future Enhancements


Instead of passing the red, green, and blue separately I could have combined two of the colours by a similar bit shifting method as used in the buffer – I didn’t for readability, however it would have been 20% quicker to transfer data if I did make this improvement.


Conclusion


My aim was to build a cube grenade, not something useful, and I think I have succeeded in doing this. I have lost count of the number of people that have seen this and said “Why would you do that, its pointless – but you could do this with it…”


Job doneSmile

No comments: