HÄRDBÖRD: Interesting Bits

OK, so I need to get better at blog­ging on caffeine. 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 personal inse­cu­ri­ties about elec­tron­ics hack­ing becom­ing more popu­lar & acces­si­ble, making my own talents less sexy to the laywoman (and layman). That, and a ghetto DAC with three passive compo­nents hooked up. Big whoop. So here’s the next part, with actual inter­est­ing stuff.

Transmitten

would be a great name if my setup involved some sort of sensory glove and I hadn’t brutal­ized the German language, 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 fairly obvi­ous what I was trying to do here, though poor camer­a­work coughjami­soncough kind of obscured how moving the joystick on the Nunchuk controlled Jamison’s three-pound battle­bot, Cake. Wire­lessly. Anyways, a series of dyscon­scious design deci­sions led to the follow­ing desired specs:

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

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

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

Surpris­ingly enough, Paral­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 suppos­edly 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 complex­ity, part count, cost, etc. Boring stuff.

Nunchuk

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

Secondly, these things are an awesome addi­tion to any project requir­ing an analog joystick, two buttons, and a three axis gyro, mounted in an overly ergonomic hous­ing with Apple aesthet­ics and Nintendo unbreak­a­bil­ity. Take care not to drop them though—you might break the floor. Nunchuks can be accessed through a simple I²C inter­face, exposed phys­i­cally through six spring contacts in the connec­tor for which simple adapters are avail­able.

Keep in mind that the Nunchuks have gold-plated spring contacts and there is a tin finish on those adapters. This causes some sort of galvanic 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 wondered what sort of twisted inter­face code I’d come up with to grab data off of these things. Oddly enough I didn’t screw up the exter­nal inter­face that badly:

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 output and four self-explana­tory func­tions. Clean and simple.

However, my imple­men­ta­tion in the source file (attached) is way shakier, involv­ing GCC attrib­utes, bitfields, and anony­mous union-nested anony­mous structs. But hey, that’s how I roll when I want to decode binary 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 supposed to achieve? Well, it’s basi­cally a 6-byte long C struct which corre­sponds to the bit patterns 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 decoded data is swiz­zled together 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 compiler 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 regis­ters with­out janky cast­ing, and the bitfields in the nested anony­mous prevents me from having to shift or mask off bits in order to decode the Nunchuk regis­ters into my output data struct. For compar­a­tive delta, this is how the “refer­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 nested 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, making it instantly supe­rior from a soft­ware engi­neer­ing stand­point. That is, the program­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 refer­ence a couple of websites to write her own decod­ing routines before printf‘ing the data, for exam­ple.

Non-volatile storage

If there’s one thing I’m unhappy about with my choice of micro­con­troller, it’s that it doesn’t have built-in data EEPROM. EEPROM is flash memory, which is non-volatile—its contents are retained even when powered off. Most decent PICs have, in addi­tion to RAM and program memory flash (where all your code is stored), a piece of data EEPROM acting as a memory-mapped periph­eral which you can use as if it were an exter­nal EEPROM chip over SPI or I²C. The dsPIC33 family does not have this4.

But I still want my non-volatile stor­age, so I can store Nunchuk cali­bra­tion data in the trans­mit­ter. How? We write it to the program flash. To make a long story short, this is a fairly compli­cated proce­dure which was achieved by using Microchip’s Data EEPROM Emula­tion library, which turns unused program flash space into soft­ware-address­able EEPROM blocks. It even spreads writes over differ­ent blocks of allo­cated program flash in order to reduce wear on the EEPROM cells.

Trou­ble was, this library had some fairly odd soft­ware engi­neer­ing prac­tices, like putting way too much inter­nals in a public header. It also used a sepa­rate unit of assem­bly language routines so the library could use specific instruc­tions for flash reads and writes, when they could have been done in inline assem­bly, or even better, compiler built-in func­tions. It’s not like your assem­bly is more portable when you put it in a sepa­rate file, Microchip. :\

My modi­fi­ca­tions and simpli­fi­ca­tions to the library are attached to this post.

The protocol

RC control is essen­tially pack­eted 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­tional stream (yes, I know it’s pack­ets under­neath) rather than a packet inter­face like Nordic radios. For those who grok teh nets, it’s more TCP and less UDP.

So, I hacked up a proto­col that took advan­tage of both: use escape char­ac­ters and base64 encod­ing to repre­sent binary 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 pattern?

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 surrounded with non-base64 escape char­ac­ters, ^ and $, into a 10-byte ASCII string.

Now, every­thing outside of those escape chars can be easily parsed out on the receiv­ing end. So, now we have easily-decoded binary data repre­sented as ASCII so it doesn’t screw with our termi­nals, and we can stick “comments” into the stream so we can peek in and read debug output.

For exam­ple, this is what actu­ally 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 taking advan­tage of the band­width.

On the receiver end, it’s triv­ial to listen for a ^ char­ac­ter, start decod­ing base64 into another FluffyData_t struct, and then stop decod­ing upon receiv­ing a $ char­ac­ter. Of course I am over­sim­pli­fy­ing data integrity issues here, which are reme­died in my design with a few simple 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 refer­enced in the nunchuk.c code for I²C routines.

  1. With pin head­ers, Montre­sor! Spark­fun sells only head­ers at the same cost. []
  2. Really a USB to serial adapter + LDO regu­la­tor with an Xbee foot­print. []
  3. OK, they are. But still. SUPPLIES! []
  4. Though dsPIC30 does, for some reason. []
  5. HÄRDBÖRD used to be called Fluffy, in case you’re wonder­ing about the name. []
  6. Note the byte array union again, which is great for doing binary trans­forms like b64 encod­ing. []