Arduino -> Processing: Passing a 6-byte union as a packet, translating to MIDI

Hi all,

I'm trying to get data flowing between my DJ Hero and Arduino / Processing, so I can produce MIDI messages for Traktor. I've based my work on a library by Jason Leyrer which does the same thing for a Guitar Hero controller, and I hope to make my version a plug-and-play library for Arduino users with a DJ Hero controller.

I've already had some great help from Nick Gammon of this forum on arduino.stackexchange but I'm stuck again; I'm really not very experienced with all this bit-level stuff.

I have everything connected as follows:

DJ Hero controller Arduino Mega Processing Traktor software.

What I now have in the Arduino (thanks to Nick) is a nicely formatted union which equates to the six bytes of data that I'm reading from the DJ Hero.

However I'm not sure what is the best approach to send this data structure into Processing. Is it possible to send as a six-byte packet? Should I just be accessing the union's fields by name on Arduino, outputting name: value to Serial and parsing that in Processing?

Right now my output looks like this:

-- packedFields bytes --
100000
1001110
10001110
1110
0
11111111
-------------------

I think I get why each representation of a byte isn't 8-bits long (because I'm casting from Int?), but how do I get each byte into Processing intact?

I tried casting out to char and got this:

-- packedFields bytes --
100000
1001110
11111111111111111111111110001110
1110
0
11111111111111111111111111111111
-------------------

So now I am back to casting to int, then printing to Serial. The command I'm using for this is Serial.println((int) theFields.b [i], BIN);

I tried a simple bit of testing using the following code:

unsigned int oneBit = 1;

if(theFields.b.rbb == oneBit){
   Serial.print("Blue pressed");
}

if(theFields.b.rbg == oneBit){
   Serial.print("Green pressed");
}

if(theFields.b.rbr == oneBit){
   Serial.print("Red pressed");
}

But this produces the following error:

error: request for member 'rbb' in 'theFields.packedFieldsUnion::b', which is of non-class type 'uint8_t [6] {aka unsigned char [6]}

TBH I've been at this for over a week and really just want to get on with the DJing (this is all for a friend's wedding I'm playing in September); I'd really appreciate some help on getting this up and running!

Full code attached

DJ_Hero_on_Mega.zip (56.8 KB)

Just to make it easy for everyone, here is the code:

DJ_funcs.h

/*
  Wii DJ functions library - Arduino meets Wii DJ (sold/used with DJ Hero 3)

  2008 Jason Leyrer - http://www.jleyrer.net

  This library is an adaptation of Tod E. Kurt's (http://todbot.com/blog/) Wii Nunchuck
  functions lib, which uses Wii Nunchuck reading code originally from Windmeadow Labs
  (http://www.windmeadow.com/node/42).

  Questions/Suggestions? Email me at jleyrer [a] gmail.com

  */


#define DJHERO 0x52
#define BAUDRATE 19200
#define byte uint8_t

struct packedFields
{
  // byte 0
  unsigned int sx      : 5;
  unsigned int rtt_4_3 : 3;

  // byte 1
  unsigned int sy      : 5;
  unsigned int rtt_2_1 : 3;

  // byte 2
  unsigned int rtt_5   : 1;
  unsigned int cs      : 4;
  unsigned int ed_4_3  : 2;
  unsigned int rtt_0   : 1;

  // byte 3
  unsigned int ltt_4_0  : 5;
  unsigned int ed       : 3;

  // byte 4
  unsigned int ltt_5    : 1;
  unsigned int rbr      : 1;
  unsigned int b_plus   : 1;
  unsigned int filler_2 : 1;
  unsigned int b_minus  : 1;
  unsigned int lbr      : 1;
  unsigned int filler_1 : 2;

  // byte 5
  unsigned int filler_4 : 2;
  unsigned int rbb      : 1;
  unsigned int lbg      : 1;
  unsigned int be       : 1;
  unsigned int rbg      : 1;
  unsigned int filler_3 : 1;
  unsigned int lbb      : 1;

};

union packedFieldsUnion
{
  packedFields a;
  byte b [6];
};

DJ_funcs.ino

/*
   initialize the I2C system, join the I2C bus,
   and tell the DJ Hero we're talking to it
*/
static void DJ_init(packedFieldsUnion & theFields)
{
  Serial.begin(BAUDRATE);
  Serial.println();
  Serial.print (F("packedFields size = "));
  Serial.println (sizeof (packedFields));

  memset (&theFields, 0, sizeof theFields); // initialize union?

  Wire.begin();                   // join i2c bus as master
  Wire.beginTransmission(DJHERO); // transmit to device 0x52
  Wire.write(0xF0);               // sends memory address
  Wire.write(0x55);               // sends a zero.
  Wire.endTransmission();         // stop transmitting
}
/*
    Send a request for data to the DJ Hero
*/
static void DJ_send_request()
{
  Wire.beginTransmission(DJHERO); // transmit to device 0x52
  Wire.write(0x00);               // sends one byte
  Wire.endTransmission();         // stop transmitting
}

/*
   Encode data to format that most wiimote drivers except
   only needed if you use one of the regular wiimote drivers
*/
static char DJ_decode_byte (char x)
{
  x = (x ^ 0x17) + 0x17;
  return x;
}

/*
   Receive data back from the DJ,
   returns 1 on successful read. returns 0 on failure
*/
static int DJ_get_data(packedFieldsUnion & theFields)
{
  int cnt = 0;
  Wire.requestFrom (DJHERO, 6);      // request data from DJ Hero
  while (Wire.available()) {
    // receive byte as an integer
    theFields.b [cnt] = DJ_decode_byte(Wire.read());
    cnt++;
  }
  DJ_send_request();  // send request for next data payload
  // If we recieved the 6 bytes, then go print them
  if (cnt >= 5) {
    return 1;       // success
  }
  return 0;           //failure
}

/*
   Print the input data we have recieved
*/
static void DJ_print_data(packedFieldsUnion & theFields)
{
  static int i = 0;

  Serial.println (F("-- packedFields bytes --"));
  for (int i = 0; i < sizeof theFields; i++) // prints initialized values?
    Serial.println ((char) theFields.b [i], BIN);
  Serial.println("-------------------");
  Serial.print("\r\n");  // newline

  unsigned int oneBit = 1;

  //
  //if(theFields.b.rbb == oneBit){
  //    Serial.print("Blue pressed");
  //}
  //
  //if(theFields.b.rbg == oneBit){
  //    Serial.print("Green pressed");
  //}
  //
  //if(theFields.b.rbr == oneBit){
  //    Serial.print("Red pressed");
  //}
  i++;
}

DJ_Hero_on_Mega.ino

#include <SoftwareSerial.h>
#include <Wire.h>
#include "DJ_funcs.h"

packedFieldsUnion foo;

// initialize the I2C system, join the I2C bus,
// and tell the DJ Hero we're talking to it

void setup()
{
  DJ_init(foo);
  delay(1);
}

void loop()
{
  int success = DJ_get_data(foo);

  if (success) {
    DJ_print_data(foo);
  } else {
    Serial.println("Not getting data");
  }
  delay(750);
}

Is it possible to send as a six-byte packet?

Well you can, of course, but what is the point of decoding the structure then?

I would be doing the bit maths first, and then send the result.

In particular you need to assemble the RTT field because that comes in pieces.

 unsigned int rtt_4_3 : 3;
...
 unsigned int rtt_2_1 : 3;
...
 unsigned int rtt_5   : 1;
...
 unsigned int rtt_0   : 1;

So you could do something like this:

  int rtt = theFields.a.rtt_0         |
           (theFields.a.rtt_2_1 << 1) |
           (theFields.a.rtt_4_3 << 3) |
           (theFields.a.rtt_5   << 5);

That "ors" the bits together to give you a single int.

Do something similar with ED and LTT and you now have complete numbers.

Then just send them in one line with a space (or comma) between them. Then in Processing, break down each line at this delimiter.

But this produces the following error:
error: request for member 'rbb' in 'theFields.packedFieldsUnion::b', which is of non-class type 'uint8_t [6] {aka unsigned char [6]}

That was in the "a" part of the union, not the "b" part.

Headsmack. Thank you.

Sorry I initially pasted the code but couldn't find the code tag on the widget above so thought 'Teletype' was the only option!

[quote author=Nick Gammon date=1438292066 link=msg=2337817]
Well you can, of course, but what is the point of decoding the structure then?[/quote]

I think part of the desire to do this was because I'm stronger in Processing than Arduino and I wanted to debug more clearly; because of the Serial output above and some strange behaviour when I printed based on the value of a field, it looked like I was reading into the data structure incorrectly on the Arduino side. But I think this strange behaviour was probably due to me mixing up theFields.a and theFields.b ...

I wanted to somehow visualize the entire dataframe in Processing (I actually sketched up an 8*6 grid for this), and I had read somewhere that you can use a matching union on the receiving end and that it's all very easy to shuttle the same datatype back and forth (a little knowledge being a dangerous thing etc).

I would be doing the bit maths first, and then send the result.

In particular you need to assemble the RTT field because that comes in pieces.

 unsigned int rtt_4_3 : 3;

...
unsigned int rtt_2_1 : 3;
...
unsigned int rtt_5   : 1;
...
unsigned int rtt_0   : 1;




So you could do something like this:



int rtt = theFields.a.rtt_0         |
          (theFields.a.rtt_2_1 << 1) |
          (theFields.a.rtt_4_3 << 3) |
          (theFields.a.rtt_5   << 5);




That "ors" the bits together to give you a single int.

Do something similar with ED and LTT and you now have complete numbers.

Then just send them in one line with a space (or comma) between them. Then in Processing, break down each line at this delimiter.

Ok, I've seen a couple of examples of that (delimiters) so I think I can actually manage that bit.

I'll have to study the OR-ing you're doing above as it looks like sorcery to my addled brain but thank you so much for all this help; I'm going to jump for joy when I finally get this working :slight_smile:

Looking at your original diagram of the bit positions (in StackExchange) just think of the OR-ing as moving the blocks (bits) around until they are nicely side-by-side. In order to do this, some have to be shifted along into their appropriate positions.

Hi Nick, just wanted to say thanks a lot for your help on this! I'm now able to successfully read / interpret data coming from the DJ Hero at a decent speed :slight_smile:

I had to make one or two corrections to your code above (some of the numbers of bits didn't add up) and add some software debouncing- it's not yet 100% but it's close.

I've started hacking the controller too so I can add more controls & visual feedback. Might even post a project log when I'm done! Will also post the code when ready.