nike ipod sensor

Here is the code i used to decipher the nike ipod sensor secrets.
You can follow the walking and running steps taken and also the total mileage walking and running. You also get the time the unit has been live.

[spoiler]

 

/* 

 This Nike adapter code is released under the following license:
 - You may use the code for personal (non-commercial) projects free of charge on conditions that: (1) credit is given in any write-ups of the project, 
                - and (2) your code changes (if any) are made public at the same time as the project is.
 - You must contact Dmitry Grinberg (dmitrygr@gmail.com) if you would like to use this code in a commercial project.
 - If you do not agree to these terms, you may not use this code.
 Nike adapter
 A lot of hard work went into figuring all this out, so please respect the above.
*/
#include <SoftwareSerial.h>

const char serialNumChars[] = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
const int randomTable1[] = {1, 4, 15, 12, 8, 2, 3, 6, 14, 0, 5, 7, 13, 10, 9, 11};
const int randomTable2[] = {0x67, 0x6, 0x51, 0xb8, 0x4a, 0xca, 0x35, 0x3e, 0x72, 0x85, 0x12, 0xd4, 0x8e, 0x9d, 0x1c, 0x23};
char buffer[50];

#define rxPin 3
#define txPin 2
#define ledPin 13
int init1[] = {0xFF,0x55,0x04,0x09,0x07,0x00,0x25,0xC7};
int init2[] = {0xFF,0x55,0x02,0x09,0x05,0xF0};
byte answ1[] = {0xFF,0x55,0x04,0x09,0x00,0x00,0x07,0xEC};
byte answ2[] = {0xFF,0x55,0x04,0x09,0x06,0x00,0x25,0xC8};

int packet[34];
boolean flag = false; 
int frame[33];
int crc;
int i = 1;
int j = 1;
// set up a new serial port 
SoftwareSerial mySerial =  SoftwareSerial(rxPin, txPin);
byte pinState = 0;
byte someChar;

int getSrcFlags(const int* packet){
	return packet[0];
}

int getDstFlags(const int* packet){
	return(getSrcFlags(packet) & 0x80) ? packet[1] : 0;
}

int getSrcTypeLen(const int* packet){
	return packet[0] & 3;
}

int getSrcAddrLen(const int* packet){
	int ret = packet[0];
	if(!(ret & 3)) return 0;
	return ((ret & 0x0C) >> 2) + 1;
}

int getDstAddrLen(const int* packet){
	int ret = getDstFlags(packet);
	if(!(ret & 3)) return 0;
	return((ret & 0x0C) >> 2) + 1;
}

int getDstTypeLen(const int* packet){
	return getDstFlags(packet) & 3;
}

int* getPayloadPtr(int* packet){
	int L1, L2, L3;
	L1 = getSrcTypeLen(packet);
	L2 = getSrcAddrLen(packet);
	L3 = getDstTypeLen(packet) + getDstAddrLen(packet);
	L3 += L3 ? ((getDstFlags(packet) & 0x10) ? 2 : 1) : 0;
	return packet + L1 + L2 + L3 + 2;
}

static long getAddr(const int* ptr, int sz){
	int bytes[4] = {0};
	switch((sz >> 2) & 3){
		case 3:
			bytes[3] = ptr[3];
		case 2:
			bytes[2] = ptr[2];
		case 1:
			bytes[1] = ptr[1];
		case 0:
			bytes[0] = ptr[0];
	}
	return (((long)(bytes[3])) << 24) | (((long)(bytes[2])) << 16) | (((long)(bytes[1])) << 8) | ((long)(bytes[0]));
}

static long getSrcAddr(const int* packet){
	int t, f = getSrcFlags(packet);
	t = (f & 3);
	if(!t) return 0;
	packet += t + 1;
	if(f & 0x80) packet++;
	return getAddr(packet, f);
}

static long getDstAddr(const int* packet){
	int L1, L2, L3;
	L1 = getDstTypeLen(packet);
	if(!L1) return 0;
	L2 = getDstFlags(packet);
	L3 = getSrcTypeLen(packet) + getSrcAddrLen(packet);
	packet += L1 + 2 + L3;
	returngetAddr(packet, L2);
}

static int getType(const int* ptr, int len){
	int t = 0;
	switch(len){
		case 2:
			t |= ((int)ptr[1]) << 8;
		case 1:
			t |= ptr[0];
	}
	returnt;
}

static int getSrcType(const int* packet){
	return getType(packet + 2, getSrcTypeLen(packet) - 1);
}

static int getDstType(const int* packet){
	return getType(packet + getSrcTypeLen(packet) + getSrcAddrLen(packet) + 1, getDstTypeLen(packet) - 1);
}

int getPayloadLen(int* packet){
	int* payload = getPayloadPtr(packet);
	return packet + 28 - payload;
}

int getTimingByte(const int* packet){
	int L1, L2, L3;
	L1 = getSrcTypeLen(packet);
	L2 = getSrcAddrLen(packet);
	L3 = getDstTypeLen(packet);
	L3 += getDstAddrLen(packet);
	if(L3) L3++;
	return packet[L1 + L2 + L3 + 1];
}

void descramblePayload(int* packet){
	int* payload;
	int srcAddr, tR;
	int t2 = 0, t1 = 0, t3, t9 = 0x23, i;
	int payloadLen, timingByte;
	payload = getPayloadPtr(packet);
	srcAddr = getSrcAddr(packet);
	payloadLen = getPayloadLen(packet);
	timingByte = getTimingByte(packet);
	if(srcAddr & 0x0080UL) t2 |= 4;
	if(srcAddr & 0x2000UL) t2 |= 2;
	if(srcAddr & 0x0400UL) t2 |= 1;
	if(srcAddr & 0x1000UL) t1 |= 4;
	if(srcAddr & 0x0008UL) t1 |= 2;
	if(srcAddr & 0x8000UL) t1 |= 1;
	i = 0;
	tR = srcAddr >> t1;
	timingByte += t2;
	for(i = 0; i < payloadLen; i++, payload++, timingByte++){
		timingByte &= 0x0F;
		t3 = randomTable1[timingByte];
		t3 += tR;
		t3 &= 0x0F;
		t3 = randomTable2[t3];
		t3 ^= t9;
		t9 = *payload;
		*payload = t9 - t3;
		if(t1 <= 11){
			t1++;
			tR >>= 1;
		}
		else{
			t1 = 0;
			tR = srcAddr;
		}
	}
}

void calcSN(long sn, int* decodedPacket, char* dst){	//will give partial SN even without a full packet!
	int t;
	sn += 0xFF000000;
	dst[7] = serialNumChars[sn % 34];	//without the DECODED packet we can only do so much:
	sn /= 34;
	dst[6] = serialNumChars[sn % 34];
	sn /= 34;
	dst[5] = serialNumChars[sn % 34];
	sn /= 34;
	t = sn % 1156;
	sn /= 1156;
	dst[3] = serialNumChars[(t % 54) / 10];
	dst[4] = serialNumChars[(t % 54) % 10];
	dst[2] = serialNumChars[t / 54];
	dst[1] = serialNumChars[sn % 34];
	sn /= 34;
	if(decodedPacket){	//we can do more 
		int* p = getPayloadPtr(decodedPacket) + 0x10;
		long x = 0;
		x = (x << 8) | (p[2] & 0x1F);
		x = (x << 8) | p[1]&0xFF;
		x = (x << 8) | p[0]&0xFF;
		dst[10] = serialNumChars[x % 34];
		x /= 34;
		dst[9] = serialNumChars[x % 34];
		x /= 34;
		dst[8] = serialNumChars[x % 34];
		x /= 34;
		dst[0] = serialNumChars[x % 34];
	}
	else{
		dst[0] = '?';
		dst[8] = '?';
		dst[9] = '?';
		dst[10] = '?';
	}
	dst[11] = 0;
}

int getOnHours(int* payload){
	return (((int)(payload[18] & 0xE0)) << 3) + payload[1]&0xFF;
}

long getRunningStepCount(int* payload){
	long r = 0;
	r = (r << 8) | payload[12]&0xFF;
	r = (r << 8) | payload[11]&0xFF;
	r = (r << 8) | payload[10]&0xFF;
	return r;
}

long getWalkingStepCount(int* payload){
	long r = 0;
	r = (r << 8) | payload[6]&0xFF;
	r = (r << 8) | payload[5]&0xFF;
	r = (r << 8) | payload[4]&0xFF;
	return r;
}

long getLifetimeRunningMiles(int* payload){ // mul by 64 and div by 18947.368115186691 to get actual miles
	long r = 0;
	r = (r << 8) | payload[15]&0xFF;
	r = (r << 8) | payload[14]&0xFF;
	r = (r << 8) | payload[13]&0xFF;
	returnr;
}

long getLifetimeWalkingMiles(int* payload){	// mul by 64 and div by 15319.148451089859 to get actual miles
	long r = 0;
	r = (r << 8) | payload[9]&0xFF;
	r = (r << 8) | payload[8]&0xFF;
	r = (r << 8) | payload[7]&0xFF;
	return r;
}

int getTc(int* payload){	//what the hell is "Tc" anyways?
	int r = 0;
	r = (r << 8) | payload[3]&0xFF;
	r = (r << 8) | payload[2]&0xFF;
	return r & 0x7FF;
}

const char* getDeviceType(int device){
	switch(device){
		case 0x01:
			return "Polar heart rate monitor";
                        break;
		case 0x06:
			return "Nike+ foot sensor";
                        break;
		case 0x0D:
			return "Nike+ Amp+ remote";
                        break;
		default:
			return "Unknown device";
	}
}

void printd(const int* d, int sz){
	while(sz)  
        {
         sprintf(buffer,(sz--) ? "%02X" : "%02X", *d++ &0xFF); Serial.print(buffer);
        }
}

int hexv(char c){
	if(c >= '0' && c <= '9') return c - '0';
	if(c >= 'A' && c <= 'F') return c + 10 - 'A';
	if(c >= 'a' && c <= 'f') return c + 10 - 'a';
	return -1;
}
void process(int* packet){
	char sn[12];
	int* payload;
	descramblePayload(packet);
	payload = getPayloadPtr(packet);
	calcSN(getSrcAddr(packet), packet, sn);
	Serial.print("  FRAME:n");
	sprintf(buffer,"    -> src flags: 0x%02Xn", getSrcFlags(packet));Serial.print(buffer);
	sprintf(buffer,"    -> src type len: 0x%02Xn", getSrcTypeLen(packet));Serial.print(buffer);
	sprintf(buffer,"    -> src type: 0x%04Xn", getSrcType(packet));Serial.print(buffer);
	sprintf(buffer,"    -> src addr len: 0x%02Xn", getSrcAddrLen(packet));Serial.print(buffer);
	sprintf(buffer,"    -> src addr: 0x%08lXn", getSrcAddr(packet));Serial.print(buffer);
	sprintf(buffer,"    -> dst flags: 0x%02Xn", getDstFlags(packet));Serial.print(buffer);
	sprintf(buffer,"    -> dst type len: 0x%02Xn", getDstTypeLen(packet));Serial.print(buffer);
	sprintf(buffer,"    -> dst type: 0x%04Xn", getDstType(packet));Serial.print(buffer);
	sprintf(buffer,"    -> dst addr len: 0x%02Xn", getDstAddrLen(packet));Serial.print(buffer);
	sprintf(buffer,"    -> dst addr: 0x%08lXn", getDstAddr(packet));Serial.print(buffer);
	sprintf(buffer,"    -> timing byte = 0x%02Xn", getTimingByte(packet));Serial.print(buffer);
	sprintf(buffer,"    -> payload len = 0x%02Xn", getPayloadLen(packet));Serial.print(buffer);
	sprintf(buffer,"  DATA:n");Serial.print(buffer);
	sprintf(buffer,"    ->packet is from device type ");Serial.print(buffer); Serial.println(getDeviceType(payload[0])); 
	if(payload[0] == 0x06){
	sprintf(buffer,"    ->packet is from device ");Serial.print(buffer);Serial.println(sn);
		sprintf(buffer,"    ->Nike+ foot pod data:n");Serial.print(buffer);
		sprintf(buffer,"        ->on hours: ");Serial.print(buffer);Serial.println(getOnHours(payload));
		sprintf(buffer,"        ->Tc: ");Serial.print(buffer);Serial.println(getTc(payload));
		sprintf(buffer,"        ->walking steps: %lun", getWalkingStepCount(payload));Serial.print(buffer);
		sprintf(buffer,"        ->running steps: %lun", getRunningStepCount(payload));Serial.print(buffer);
		sprintf(buffer,"        -> lifetime walking miles: %lun", getLifetimeWalkingMiles(payload));Serial.print(buffer);
		sprintf(buffer,"        -> lifetime running miles: %lun", getLifetimeRunningMiles(payload));Serial.print(buffer);
	}
	sprintf(buffer,"    ->raw payload: ");Serial.print(buffer);
	printd(payload, getPayloadLen(packet));
	sprintf(buffer,"n");Serial.print(buffer);

}

void setup()  {
  // define pin modes for tx, rx, led pins:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  // set the data rate for the SoftwareSerial port
  mySerial.begin(57600);
  Serial.begin(57600);
  Serial.print("nStart");
do {
  flag = false;
  Serial.print("nTransmit  --> FF 55 04 09 07 00 25 C7");
  for (int i=0;i<8;i++){mySerial.write(init1[i]);}  
  Serial.print("nExpecting <-- FF 55 04 09 00 00 07 EC");
  Serial.print("nAnswer is <-- ");
  for (int i=0;i<8;i++){
  someChar = byte (mySerial.read());
  if (someChar != answ1[i]) {flag = true;}
  if (someChar <0x10) Serial.print("0");
  Serial.print(someChar,HEX);Serial.print(" ");
  }
  Serial.print("nTransmit  --> FF 55 02 09 05 F0");
  for (int i=0;i<6;i++){mySerial.write(init2[i]);}  
  Serial.print("nExpecting <-- FF 55 04 09 06 00 25 C8");
  Serial.print("nAnswer is <-- ");
  for (int i=0;i<8;i++){
  someChar = mySerial.read();
  if (someChar != answ2[i]) {flag = true;}
  if (someChar <0x10) Serial.print("0");
  Serial.print(someChar,HEX);Serial.print(" ");
  }
} while (flag == true);
  Serial.print("nOK now innitialized and listening for steps!!!!n");
}

void loop() 
{ 

  if (mySerial.available() > 0)
  {
       Serial.print("nNow receiving !!!!!!!!!!!!!!!!!n");
        for(int i = 0 ; i < 34 ; i++) //Capture the 32 character frame returned by the iPod receiver
        {
          frame[i] = mySerial.read();
          if (frame[i] <0x10) Serial.print("0");
          Serial.print(frame[i],HEX);
        }        
        Serial.println();
  }
  else {
    frame[0]=0;frame[1]=0;
  }
if (frame[0] == 0xFF & frame[1] == 0x55) interpret();
}  

void toggle(int pinNum) {
  // set the LED pin using the pinState variable:
  digitalWrite(pinNum, pinState); 
  // if pinState = 0, set it to 1, and vice versa:
  pinState = !pinState;
}

void interpret(){
   // test[] contains full string including leading FF 55 and checkbyte for the Nike adapter
  Serial.print("Data received OK !!!n");
  crc = 0;
  for (int i=2; i<33;i++){crc +=frame[i];}//CRC is all bytes directly after FF 55 to one before checksum for the Nike adapter
  crc = 0x100 - (crc &0xFF);
  if (crc == frame[33]) Serial.print("CRC OKn");// do something if CRC not OK
  for (int i=5;i<33;i++){
    packet[i-5] = frame[i];// move and strip to packet for analysis
    if (packet[i-5] <0x10) Serial.print("0");
    Serial.print(packet[i-5],HEX);
  }
  Serial.println();
  sprintf(buffer,"Packet %d:n", j);Serial.print(buffer);
  process(packet);
  j++;
  toggle(ledPin);
}
[/spoiler]

And here you can download it.

nike ipod sensor links:

Reverse Engineering the Nike iPod Protocol Never Use This Font
iPodLinux Apple Accessory Protocol Nike  iPod Linux
Nike Store Nederlands. Polar Wearlink Transmitter
Dmitry Grinberg on the Nike adapter
http://www.cs.washington.edu/research/systems/privacy.html
http://web.student.tuwien.ac.at/~e0026607/ipod_remote/ipod_ap.html
http://ipl.derpapst.eu/wiki/User:Davandron
https://www.sparkfun.com/products/8245
http://smus.com/nike-hacking-with-python/
http://www.apple.com/ipod/nike/ Here you can buy the nike adapter

Leave a Reply