objective-ccocoaroboticslego-mindstorms

Lego Mindstorm NXT, Cocoa, and HiTechnic Sensors


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.


Solution

  • 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