HÄRDBÖRD: Interesting Bits

OK, so I need to get bet­ter at blog­ging on caf­feine. I spent about four times the amount of time writ­ing the last blog post on HÄRDBÖRD than I did work­ing on the stuff described in it. And let’s face it, what I described were my per­son­al inse­cu­ri­ties about elec­tron­ics hack­ing becom­ing more pop­u­lar & acces­si­ble, mak­ing my own tal­ents less sexy to the lay­wom­an (and lay­man). That, and a ghet­to DAC with three pas­sive com­po­nents hooked up. Big whoop. So here’s the next part, with actu­al inter­est­ing stuff.

Transmitten

would be a great name if my setup involved some sort of sen­so­ry glove and I hadn’t bru­tal­ized the Ger­man lan­guage, but it doesn’t and I have…

The transmitter

I could explain the design, but I think this video is more help­ful (though not very help­ful):

I think it’s fair­ly obvi­ous what I was try­ing to do here, though poor cam­er­a­work coughjamisoncough kind of obscured how mov­ing the joy­stick on the Nunchuk con­trolled Jamison’s three-pound bat­tle­bot, Cake. Wire­less­ly. Any­ways, a series of dyscon­scious design deci­sions led to the fol­low­ing desired specs:

  • Wire­less con­trol of the BÖRDs
  • Wii Nunchuk as the human inter­face
  • Some sort of reli­able 2.4GHz link (radio hard­ware with a pro­to­col stack built in)

Every­thing else fell into place right after that. I grabbed two pairs of the low­est-end Xbee radio trans­ceivers as well as the cheap­est USB adapters and break­out boards I could find for them. “O Cap­tain,” you cry, “that’ll be from Spark­fun, right?”

Yeah no, Spark­fun is for tools, man. They were so much bet­ter when they were under­ground. </hípster>

Sur­pris­ing­ly enough, Par­al­lax has Xbees at the same price as Digi-Key/Mouser, $3 break­out adapter boards1, and a $20 USB break­out2. And you thought they’re those over­priced back­wards BASIC Stamp kids who sup­pos­ed­ly got super­seded by the Arduino clus­terfugue3.

The rest of the arti­cle is about the tech­ni­cal hacks I used in soft­ware to reduce com­plex­i­ty, part count, cost, etc. Bor­ing stuff.

Nunchuk

First off, it’s “nunchuk.” Not “nunchuck,” which is what you do in a con­vent.

Sec­ond­ly, the­se things are an awe­some addi­tion to any project requir­ing an analog joy­stick, two but­tons, and a three axis gyro, mount­ed in an over­ly ergonom­ic hous­ing with Apple aes­thet­ics and Nin­ten­do unbreak­a­bil­i­ty. Take care not to drop them though—you might break the floor. Nunchuks can be accessed through a sim­ple I²C inter­face, exposed phys­i­cal­ly through six spring con­tacts in the con­nec­tor for which sim­ple adapters are avail­able.

Keep in mind that the Nunchuks have gold-plat­ed spring con­tacts and there is a tin fin­ish on those adapters. This caus­es some sort of gal­van­ic reac­tion and you get a lot of streaky black residue on both after a few months of use. Ew.

Now, I haven’t writ­ten straight C for a while now, so I won­dered what sort of twist­ed inter­face code I’d come up with to grab data off of the­se things. Odd­ly enough I didn’t screw up the exter­nal inter­face that bad­ly:

typedef struct {
	uint8 joyX;
	uint8 joyY;
	uint16 accelX;
	uint16 accelY;
	uint16 accelZ;
	uint8 buttonZ;
	uint8 buttonC;
} NunchukData_t;

void nunchukInit(void);
int nunchukReadData(void);
NunchukData_t nunchukGetData(void);
void nunchukPrintData(void);

Yeah, so if you were to use my code, you’d see a C struct for the data out­put and four self-explana­to­ry func­tions. Clean and sim­ple.

How­ev­er, my imple­men­ta­tion in the source file (attached) is way shakier, involv­ing GCC attrib­ut­es, bit­fields, and anony­mous union-nest­ed anony­mous structs. But hey, that’s how I roll when I want to decode bina­ry data. First I define this… thing:

typedef struct __attribute__((__packed__)) {
	union {
		struct {
			uint8 joyX;
			uint8 joyY;
			uint8 accelX_HI;
			uint8 accelY_HI;
			uint8 accelZ_HI;
			uint8 buttonZ   : 1;
			uint8 buttonC   : 1;
			uint8 accelX_LO : 2;
			uint8 accelY_LO : 2;
			uint8 accelZ_LO : 2;
		};

		uint8 buffer[6];
	};
} NunchukRegisters_t;

What is that sup­posed to achieve? Well, it’s basi­cal­ly a 6-byte long C struct which cor­re­sponds to the bit pat­terns read off of the Nunchuk. Based on read­ing mate­ri­als, I know that the data is encoded/ciphered by the byte, and all the decod­ed data is swiz­zled togeth­er to save band­width.

Thus, I have a union between the afore­men­tioned struct and a size 6 byte array so I can fill up and decode the struct in a per-byte fash­ion, just by loop­ing over the unioned array. The __attribute__((__packed__)) is just an futile, unnec­es­sary hint to my com­pil­er to make sure it doesn’t pad out my struct with extra­ne­ous bytes.

How do I use this struct? Now this is the beau­ti­ful part:

int nunchukReadData(void) {
	NunchukRegisters_t nunchukRegs; // allocate space
	readNI2C1(NUNCHUK_ADDR, nunchukRegs.buffer, 6); // read from nunchuk

	int i;
	for(i = 0; i < 6 ; i++) { // decode by byte
		nunchukRegs.buffer[i] = nunchukByteDecode(nunchukRegs.buffer[i]);
	}
	// assemble raw data
	nunchukData.joyX = nunchukRegs.joyX;
	nunchukData.joyY = nunchukRegs.joyY;
	nunchukData.accelX = (nunchukRegs.accelX_HI << 2) | nunchukRegs.accelX_LO;
	nunchukData.accelY = (nunchukRegs.accelY_HI << 2) | nunchukRegs.accelY_LO;
	nunchukData.accelZ = (nunchukRegs.accelZ_HI << 2) | nunchukRegs.accelZ_LO;
	nunchukData.buttonZ = !nunchukRegs.buttonZ;
	nunchukData.buttonC = !nunchukRegs.buttonC;

	DELAY_US(50); // comm delay so nunchuk doesn't complain
	nunchukRequestData(); // signal nunchuk to read sensors again
	return 1;
}

Notice how the union­ing lets me fill up the reg­is­ters with­out janky cast­ing, and the bit­fields in the nest­ed anony­mous pre­vents me from hav­ing to shift or mask off bits in order to decode the Nunchuk reg­is­ters into my out­put data struct. For com­par­a­tive delta, this is how the “ref­er­ence imple­men­ta­tion” looks on the Arduino:

// from http://todbot.com/arduino/sketches/WiichuckDemo/nunchuck_funcs.h
int joy_x_axis = nunchuck_buf[0];
int joy_y_axis = nunchuck_buf[1];
int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;

int z_button = 0;
int c_button = 0;

// byte nunchuck_buf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if((nunchuck_buf[5] >> 0) & 1)
	z_button = 1;
if((nunchuck_buf[5] >> 1) & 1)
	c_button = 1;

if((nunchuck_buf[5] >> 2) & 1)
	accel_x_axis += 2;
if((nunchuck_buf[5] >> 3) & 1)
	accel_x_axis += 1;

if((nunchuck_buf[5] >> 4) & 1)
	accel_y_axis += 2;
if((nunchuck_buf[5] >> 5) & 1)
	accel_y_axis += 1;

if((nunchuck_buf[5] >> 6) & 1)
	accel_z_axis += 2;
if((nunchuck_buf[5] >> 7) & 1)
	accel_z_axis += 1;

Yeah… I’d rather have nest­ed anony­mous structs than line after line of nasty bit twid­dling. Let’s ignore the fact that the above exam­ple is a snip­pet of appli­ca­tion code, while my decod­ing was library code, mak­ing it instant­ly supe­ri­or from a soft­ware engi­neer­ing stand­point. That is, the pro­gram­mer who uses my code can pick up and run with an inter­face which is decou­pled from how bits are swiz­zled by the Nunchuk. But, the coder above would have to ref­er­ence a cou­ple of web­sites to write her own decod­ing rou­ti­nes before printf‘ing the data, for exam­ple.

Non-volatile storage

If there’s one thing I’m unhap­py about with my choice of micro­con­troller, it’s that it doesn’t have built-in data EEPROM. EEPROM is flash mem­o­ry, which is non-volatile—its con­tents are retained even when pow­ered off. Most decent PICs have, in addi­tion to RAM and pro­gram mem­o­ry flash (where all your code is stored), a piece of data EEPROM act­ing as a mem­o­ry-mapped periph­er­al which you can use as if it were an exter­nal EEPROM chip over SPI or I²C. The dsPIC33 fam­i­ly does not have this4.

But I still want my non-volatile stor­age, so I can store Nunchuk cal­i­bra­tion data in the trans­mit­ter. How? We write it to the pro­gram flash. To make a long sto­ry short, this is a fair­ly com­pli­cat­ed pro­ce­dure which was achieved by using Microchip’s Data EEPROM Emu­la­tion library, which turns unused pro­gram flash space into soft­ware-address­able EEPROM blocks. It even spreads writes over dif­fer­ent blocks of allo­cat­ed pro­gram flash in order to reduce wear on the EEPROM cells.

Trou­ble was, this library had some fair­ly odd soft­ware engi­neer­ing prac­tices, like putting way too much inter­nals in a pub­lic head­er. It also used a sep­a­rate unit of assem­bly lan­guage rou­ti­nes so the library could use speci­fic instruc­tions for flash reads and writes, when they could have been done in inline assem­bly, or even bet­ter, com­pil­er built-in func­tions. It’s not like your assem­bly is more portable when you put it in a sep­a­rate file, Microchip. :\

My mod­i­fi­ca­tions and sim­pli­fi­ca­tions to the library are attached to this post.

The protocol

RC con­trol is essen­tial­ly pack­et­ed data; each piece of data you send is a full state of what motors/LEDs should be doing what, with no depen­dence on past or future data. Xbees provide you with a bidi­rec­tion­al stream (yes, I know it’s pack­ets under­neath) rather than a pack­et inter­face like Nordic radios. For those who grok teh nets, it’s more TCP and less UDP.

So, I hacked up a pro­to­col that took advan­tage of both: use escape char­ac­ters and base64 encod­ing to rep­re­sent bina­ry pack­ets, and human-read­able ASCII which is ignored by the receiver.

First I define a data struc­ture. Start­ing to see a pat­tern?

typedef struct {
	union {
		struct {
			uint16 speed;
			uint8 reversed;
			uint8 brake;
		};
		uint8 buffer[4];
	};
} FluffyData_t;

FluffyData_t5 is the infor­ma­tion the receiver cares about: speed, direc­tion, and whether to brake. This is put through a base64 encode6, and sur­round­ed with non-base64 escape char­ac­ters, ^ and $, into a 10-byte ASCII string.

Now, every­thing out­side of those escape chars can be eas­i­ly parsed out on the receiv­ing end. So, now we have eas­i­ly-decod­ed bina­ry data rep­re­sent­ed as ASCII so it doesn’t screw with our ter­mi­nals, and we can stick “com­ments” into the stream so we can peek in and read debug out­put.

For exam­ple, this is what actu­al­ly goes through my Xbees:

(Accel) x: 664, y: 450, z: 612 (Joystick) x: 148, y: 196 (Button) z: 1, c: 0
(fluffy) speed: 46260, reversed: 0, brake: 0
^tLQAAA==$
(Accel) x: 664, y: 450, z: 614 (Joystick) x: 148, y: 197 (Button) z: 1, c: 0
(fluffy) speed: 47031, reversed: 0, brake: 0
^t7cAAA==$

That’s right, most of it is human read­able. Only a tiny frac­tion of it is for the machine. This is called tak­ing advan­tage of the band­width.

On the receiver end, it’s triv­ial to lis­ten for a ^ char­ac­ter, start decod­ing base64 into anoth­er Fluffy­Da­ta_t struct, and then stop decod­ing upon receiv­ing a $ char­ac­ter. Of course I am over­sim­pli­fy­ing data integri­ty issues here, which are reme­died in my design with a few sim­ple checks and CRC16 check­sums.

Code listings

Note that I use the excel­lent Reese/Jones/Bruce library for PIC24 & dsPIC33 as my hard­ware inter­face library, which is ref­er­enced in the nunchuk.c code for I²C rou­ti­nes.

  1. With pin head­ers, Mon­tre­sor! Spark­fun sells only head­ers at the same cost. []
  2. Real­ly a USB to seri­al adapter + LDO reg­u­la­tor with an Xbee foot­print. []
  3. OK, they are. But still. SUPPLIES! []
  4. Though dsPIC30 does, for some rea­son. []
  5. HÄRDBÖRD used to be called Fluffy, in case you’re won­der­ing about the name. []
  6. Note the byte array union again, which is great for doing bina­ry trans­forms like b64 encod­ing. []