I've taken the existing code from this project, and very happy with it so far.
I'm now however in a position where I need to make use of some third-party sensors which I've purchased from hitechnic, such as an accelerometer, a gyroscope, and a 3D compass - to mention a few.
I'm not sure where to start now, but what I need to do is add to my existing code base (which is based on the this), and effectively glue my framework to the new hardware.
Can anyone point me in the right direction? I can't find any APIs from the device manufacturer, (but I have emailed them and asked - no reply yet).
I've also started to document my findings on this page.
Okay, I've had a look. The analog sensors, such as the Gyro, are dead easy...
I pretty much just reused that of another analog sensor - the Light sensor...
- (void)setupGyroscopicSensor:(UInt8)port {
[self setInputMode:port
type:kNXTGyroscope
mode:kNXTRawMode];
}
For polling, I used the generic polling method...
- (void)pollSensor:(UInt8)port interval:(NSTimeInterval)seconds;
...from the LegoNXTRemote code.
The digital ones are not as easy - specially to someone who has zero sw/hw experience. Here is the working ultrasonic sensor code, setup, and for polling. I will only write the prototype of these methods and a git clone for those interested in the full code at the end.
- (void)setupUltrasoundSensor:(UInt8)port continuous:(BOOL)continuous;
- (void)getUltrasoundByte:(UInt8)port byte:(UInt8)byte;
- (void)pollUltrasoundSensor:(UInt8)port interval:(NSTimeInterval)seconds;
Note how it has it's own dedicated method for polling. So, now the question is how to write one for the accelerometer.
The information you get when buying the sensor is a table mapping addresses to content:
42H (byte) -> X-axis upper 8 bits
43H (byte) -> X-axis upper 8 bits
44H (byte) -> X-axis upper 8 bits
45H (byte) -> X-axis lower 8 bits
46H (byte) -> X-axis lower 8 bits
47H (byte) -> X-axis lower 8 bits
...looking at the ultrasonic sensor, I can see references to 0x42
- which I am guessing is where the address goes, but that's all i can guess right now.
I'll let you know if I get any progress on this.
Okay, here's where it's at with the Accelerometer.
I first send the device the following message...
0x07, 0x00, 0x00, 0x0f, 0x03, 0x02, 0x08, 0x02, 0x42
What that means respectively (I'm could well be wrong) is...
kNXTRawMode
kNXTGetInputValues
kNXTRet //. Meaning we expect a return value
kNXTLSWrite //. As opposed to read
port //. Port 0x03 --> Port 4
txLength
rxLength
//. message...
0x02 //. Set the I2C slave address
0x42 //. Set the register we're interested in
Next we send a read request...
0x03, 0x00, 0x00, 0x0e, 0x03
And to that we get a response...
0x03, 0x00, 0x02, 0x0f, 0xe0
...and that ends with an error.
Here's a chunk of log...
libNXT[0x02]: Attempting to connect to NXT...
libNXT[0x02]: Open sequence initiating...
libNXT[0x02]: Channel Opening Completed
libNXT[0x08]: >>> :0x06, 0x00, 0x80, 0x03, 0x0b, 0x02, 0xf4, 0x01,
libNXT[0x08]: >>> :0x02, 0x00, 0x00, 0x0b,
libNXT[0x08]: <<< :0x05, 0x00, 0x02, 0x0b, 0x00, 0x82, 0x1e,
libNXT[0x08]: @selector does NOT respond to NXTOperationError:operation:status:
libNXT[0x08]: @selector responds to NXTBatteryLevel:batteryLevel:
startPollingSensor: setup sensor
startPollingSensor: start polling
libNXT[0x02]: Polling Port 3
libNXT[0x08]: >>> :0x07, 0x00, 0x00, 0x0f, 0x03, 0x02, 0x08, 0x02, 0x42,
libNXT[0x08]: >>> :0x03, 0x00, 0x00, 0x0e, 0x03,
libNXT[0x08]: <<< :0x03, 0x00, 0x02, 0x0f, 0xe0,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xf status=0xe0
libNXT[0x08]: <<< :0x04, 0x00, 0x02, 0x0e, 0xe0, 0x00,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xe status=0xe0
libNXT[0x08]: @selector does NOT respond to NXTOperationError:operation:status:
libNXT[0x02]: Polling Port 3
libNXT[0x08]: >>> :0x07, 0x00, 0x00, 0x0f, 0x03, 0x02, 0x08, 0x02, 0x42,
libNXT[0x08]: >>> :0x03, 0x00, 0x00, 0x0e, 0x03,
libNXT[0x08]: <<< :0x03, 0x00, 0x02, 0x0f, 0xe0,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xf status=0xe0
libNXT[0x08]: <<< :0x04, 0x00, 0x02, 0x0e, 0xe0, 0x00,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xe status=0xe0
libNXT[0x08]: @selector does NOT respond to NXTOperationError:operation:status:
libNXT[0x02]: Polling Port 3
libNXT[0x08]: >>> :0x07, 0x00, 0x00, 0x0f, 0x03, 0x02, 0x08, 0x02, 0x42,
libNXT[0x08]: >>> :0x03, 0x00, 0x00, 0x0e, 0x03,
libNXT[0x08]: <<< :0x03, 0x00, 0x02, 0x0f, 0xe0,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xf status=0xe0
libNXT[0x08]: <<< :0x04, 0x00, 0x02, 0x0e, 0xe0, 0x00,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xe status=0xe0
libNXT[0x08]: @selector does NOT respond to NXTOperationError:operation:status:
Error while running hook_stop:
libNXT[0x08]: >>> :0x03, 0x00, 0x00, 0x0e, 0x03,
libNXT[0x08]: <<< :0x03, 0x00, 0x02, 0x0f, 0xe0,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xf status=0xe0
libNXT[0x08]: <<< :0x04, 0x00, 0x02, 0x0e, 0xe0, 0x00,
libNXT[0x08]: @selector responds to NXTOperationError:operation:status:
nxt error: operation=0xe status=0xe0
This is all based on the example code from here, which is as follows...
SetSensorLowspeed(IN_1);
int count;
int xval;
int yval;
int zval;
byte inI2Ccmd[];
byte outbuf[];
while (TRUE) {
ArrayInit(inI2Ccmd, 0, 2); // set the buffer to hold 10 values (initially all are zero)
inI2Ccmd[0] = 0x02; // set values in the array
inI2Ccmd[1] = 0x42;
count=8; //read count set to 8 bytes
I2CBytes(IN_1, inI2Ccmd, count, outbuf); //read the acceleration sensor on port 1
xval=outbuf[0]; //load x axis upper 8 bits
yval=outbuf[1]; //load Y axis upper 8 bits
zval=outbuf[2]; //load z axis upper 8 bits
if (xval > 127) xval-=256; //convert x to 10 bit value
xval=xval*4 + outbuf[3];
if (yval > 127) yval-=256; //convert y to 10 bit value
yval=yval*4 + outbuf[4];
if (zval > 127) zval-=256; //convert z to 10 bit value
zval=zval*4 + outbuf[5];
...
}
Awesome! Looks like it's working now - I just need to fiddle with the output to extract the actual X, Y and Z readings.
If it does work, I'll let you all know, but until I've proved it, I'll leave this ticket open.
Okay, it looks like it's now working, but there's enough error in the sensor, and I've yet to prove that I've really resolved this. Here's the code snippet:
SInt8 *outbuf = malloc(48);
[data getBytes:outbuf length:6];
SInt16 x = outbuf[0]; x <<= 2; x += outbuf[3];
SInt16 y = outbuf[1]; y <<= 2; y += outbuf[4];
SInt16 z = outbuf[2]; z <<= 2; z += outbuf[5];
free(outbuf);
[self setSensorTextField:port
value:[NSString stringWithFormat:@"<%d, %d, %d>",
x, y, z]];
If anyone is interested in this, I invite you to download the source and try it out - I'm yet to prove scientifically that this is actually correct, even though first glance it looks ok.
Okay, I've done some testing - it looks good. I've converted the values to G's, as per the instructions that came with the device - stating that 1 G ~ 200 units (I wish they did a little better than ~200, some indication of the error would have been nice).
//. Acceleration in G's
SInt8 *outbuf = malloc(48);
[data getBytes:outbuf length:6];
SInt16 x = outbuf[0]; x <<= 2; x += outbuf[3]; float gX = x/200.f;
SInt16 y = outbuf[1]; y <<= 2; y += outbuf[4]; float gY = y/200.f;
SInt16 z = outbuf[2]; z <<= 2; z += outbuf[5]; float gZ = z/200.f;
free(outbuf);
[self setSensorTextField:port
value:[NSString stringWithFormat:@"%0.2f, %0.2f, %0.2f",
gX, gY, gZ]];
If you position the device as per the vendor page, you can see each access hit an acceleration reading of ~ 1.02f.
I think I can close this off now and work on cleaning up the framework.
The code can be checked out at:
git clone git://git.autonomy.net.au/nimachine Nimachine