I2C communication

edited October 2016 in General
Hi all,

I have a MetaWear RG device:
Model Number: 1
Firmware Revision: 1.2.5
Hardware Revision: 0.3

I use the Metawear-SampleAndroid App with metawear library version 2.6.5 to communicate with the MetaWear RG device.

The goal is to send commands to a NFC device, which is attached to the MetaWear over the I2C bus.

The electrical connection seems to be OK. If I write 0x01and read 1 byte, then I get a 0x00.
byte[] cmd = new byte[]{0x01,};
System.out.println("Write CMD with length: " + cmd.length);

i2cModule.writeData(deviceAddr, registerAddr, cmd);
Thread.sleep(100);

System.out.println("Try to read: ");
i2cModule.readData(deviceAddr, registerAddr, (byte) 7).onComplete(new CompletionHandler<byte[]>() {
@Override
public void success(byte[] result) {
System.out.println("Result: " + arrayToHexString(result));
}
});
The resulting output is:
System.out: Device: 0x24 Register: 0x00
System.out: Write CMD with length: 1
System.out: Try to read:
System.out: Result:  0x00 0x80 0x80 0x80 0x80 0x80 0x80

Now, when I try to send a real command to the device, I get nothing back, there is simply no response.

This is the code I use:
http://pastebin.com/yLXH6Yq6

This resulting output is:
10-19 20:54:37.265 19738-19738/com.mbientlab.metawear.app I/System.out: Device: 0x24 Register: 0x00
10-19 20:54:37.265 19738-19738/com.mbientlab.metawear.app I/System.out: Write CMD with length: 9
10-19 20:54:37.770 19738-19738/com.mbientlab.metawear.app I/System.out: Try to read:


I've tested the NFC device with my Raspberry, so the NFC device is working fine. On the Raspberry I used a small NodeJS script to send the same command to the NFC device over I2C.

var sleep = require('sleep');
var util = require('util');
var pn532 = require('pn532');
var i2c = require('i2c');

function toBuffer(bytes) {
    return new Buffer(bytes);
}

var wire = new i2c(pn532.I2C_ADDRESS, {device: '/dev/i2c-1'});

// firmware                                                                                                                                              
var byteArray = [0x00, 0x00, 0xff, 0x02, 0xfe, 0xd4, 0x02, 0x2a, 0x00];
console.log("Write Bytes: ", toBuffer(byteArray));

function cmdFw() {
    wire.write(byteArray , function(err) {
        console.error("Error: " + err);
    });
}

function ack() {
    wire.read(7, function(err, res) {
        // result contains a buffer of bytes                                                                                                             
        console.log("Read Ack: ", toBuffer(res));
    });
}

function resFw() {
    wire.read(20, function(err, res) {
        // result contains a buffer of bytes                                                                                                             
        console.log("Read FW: ", toBuffer(res));
    });

}

cmdFw();
setTimeout(function() {
    ack();
    setTimeout(resFw, 100);
}, 100);

The resulting output is:

Write Bytes:  <Buffer 00 00 ff 02 fe d4 02 2a 00>
Error: null
Read Ack:  <Buffer 81 00 00 ff 00 ff 00>
Read FW:  <Buffer 81 00 00 ff 06 fa d5 03 32 01 06 07 e8 00 00 00 00 00 00 00>

This is also the expected output as documented in:
https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf

1. send the command
2. read acknowledge
2. read the result of the command

I would like to know how to debug the issue?


I look forward to receiving your continued support.

Comments

  • edited October 2016
    I did some experiments with the NodeJS script. It shows, if I send a wrong command I get the result:
    Write Bytes:  <Buffer 00 aa ff 02 fe d4 02 2a ff>
    Error: null
    Read Ack:  <Buffer 00 80 80 80 80 80 80>
    Read FW:  <Buffer 00 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80>

    This is the same pattern as with MetaWear sending a
    byte[] cmd = new byte[]{0x01};
    My experiments with MetaWear I2C show if I send:
    byte[] cmd = new byte[]{
    (byte) 0x00,
    (byte) 0x00,
    (byte) 0xAA
    };
    the result is:
    System.out: Device: 0x24 Register: 0x00
    System.out: Write CMD with length: 3
    System.out: Try to read:
    System.out: Result:  0x00 0x80 0x80 0x80 0x80 0x80 0x80

    if I send
    byte[] cmd = new byte[]{
    (byte) 0x00,
    (byte) 0x00,
    (byte) 0xFF
    };
    then there is no response and I need to disconnect the MetaWear from the NFC and connect it again to get a successful communication with the NFC device.

    My conclusion is that there is some problem with sending 0xFF.




  • The I2C bus does not handle asynchronous data; data will only be send if explicitly read.
  • @Eric, yes thats true :-)

    http://pastebin.com/yLXH6Yq6

    see my code in line 33
    i2cModule.readData(deviceAddr, registerAddr, (byte) nBytes).onComplete(new CompletionHandler<byte[]>()

    first write data, then read data
    this is what I did


  • Ah my apologies, I completely missed the pastebin link.  I'll try to create an equivalent test case with what we have in the office so the firmware dev can look into it further.
  • hi Eric, are there any news about the issue?
  • Sorry, I haven't had a chance to look into the issue any further.
  • edited November 2016
    I tried writing 0xff to a few of the on board sensors via the I2C bus and didn't see any issues.  On an R board, I wrote [0xfd, 0xfe, 0xff] to the XYZ offset registers on the MMA8452Q chip then read them back with no problems.
    i2cModule.writeData((byte) 0x1c, (byte) 0x2f, new byte[] {(byte) 0xfd, (byte) 0xfe, (byte) 0xff});
    Thread.sleep(500);
    i2cModule.readData((byte) 0x1c, (byte) 0x2f, (byte) 3)
    .onComplete(new CompletionHandler() {
    @Override
    public void success(byte[] result) {
    Log.i("test", Arrays.toString(result));
    }
    });

    Using an RPro board, I wrote 2 config registers (0xf4, & 0xf5) and read it back with no issues as well.
    i2cModule.writeData((byte) 0x77, (byte) 0xf4, new byte[] {(byte) 0xff, 0x00});
    Thread.sleep(500);
    i2cModule.readData((byte) 0x77, (byte) 0xf4, (byte) 1)
    .onComplete(new CompletionHandler() {
    @Override
    public void success(byte[] result) {
    Log.i("test", Arrays.toString(result));
    }
    });
  • @didlich

    I could only look briefly at the spec for the NFC chip.  They seem to have tunneled a custom protocol through i2c in an unconventional manner.

    The big difference between your node code and the MetaWear API code, is that the MetaWear API Read performs a register read and not a raw read. 

    Basically, the register read operation first writes an address to read from, and then it reads.  This is how i2c sensors normally work.  A cursory look at the NFC datasheet suggests that the write should be ignored, but it would be best to verify the behavior is the same on the Raspberry.

    Could you try writing a single byte of 0x00 before your ack read to see how the NFC chip responds?
  • edited November 2016
    @Matt, thanks for investigating the issue

    I think you are right, if I write single byte 0x00 before ACK in RPi it fails as with MetaWear API

    I hope I understood you right, please see the nodejs code to verify:

    var sleep = require('sleep');
    var util = require('util');
    var pn532 = require('pn532');
    var i2c = require('i2c');

    function toBuffer(bytes) {
        return new Buffer(bytes);
    }

    var wire = new i2c(pn532.I2C_ADDRESS, {device: '/dev/i2c-1'});

    // firmware                                                                                                                                                  
    var byteArray = [0x00, 0x00, 0xff, 0x02, 0xfe, 0xd4, 0x02, 0x2a, 0x00];
    console.log("Write Bytes: ", toBuffer(byteArray));

    function cmdFw() {
        console.log("Write Cmd FW");
        wire.write(byteArray , function(err) {
            if (err){
                console.error("Error: " + err);
            }
        });
    }

    function ack() {
        console.log("Read ACK");
        wire.read(7, function(err, res) {
            // result contains a buffer ofbytes                                                                                                                 
            console.log("Ack: ", toBuffer(res));
        });
    }

    function resFw() {
        console.log("Read FW");
        wire.read(20, function(err, res) {
            // result contains a buffer ofbytes                                                                                                                 
            console.log("FW: ", toBuffer(res));
        });

    }

    function writeZero() {
        var zeroByteArray = [0x00];
        console.log("Write Zero");
        wire.write(zeroByteArray , function(err) {
            if (err) {
                console.error("Write Zero Error: " + err);
            }
        });
    }

    cmdFw();
    setTimeout(function() {
        writeZero();
        setTimeout(function () {
            ack();
            setTimeout(resFw, 200);
        }, 200);
    }, 200);

    The resulting output after two executions is:
    first:
    Write Bytes:  <Buffer 00 00 ff 02 fe d4 02 2a 00>
    Write Cmd FW
    Write Zero
    Read ACK
    Ack:  <Buffer >
    Read FW
    FW:  <Buffer >

    second:
    Write Bytes:  <Buffer 00 00 ff 02 fe d4 02 2a 00>
    Write Cmd FW
    Error: Error: Cannot write to device
    Write Zero
    Write Zero Error: Error: Cannot write to device
    Read ACK
    Ack:  <Buffer >
    Read FW
    FW:  <Buffer >


    Which options do I have now? As I understand, the MetaWear API do more than it should, but because the NFC-Chip has custom protocol it's not possible to use MetaWear API to cummunicate with the NFC-Chip.
    Do you have a hint for a workaround?
    Would it be feasible to provide rawRead and rawWrite methods in MetaWear API?
  • @didlich

    The simplest option would be to switch to the SPI bus for communication.  The protocol they are using is really meant for a UART and poorly adapted for I2C and SPI.  It should work with our SPI API which handles more raw operations which are typical to SPI protocols.

    A second option would be to further investigate the chips behavior in response to the I2C read format, to see if there is a workaround to get the chip to accept it.  According to statements in the datasheet the write should be ignored, but the behavior you are observing does not match.  This will require detailed study of the datasheet and experimentation.  From my brief look at the datasheet it is not among the most straightforward, I would avoid this option if possible.

    Removing the register-centric nature of MetaWear I2C would require modifications to our stack spanning several abstraction layers.  The existing implementation works for every native driver we have, as well as nearly all I2C based sensors.  Unfortunately, that means adding raw methods would be both high cost and low impact.  It would be more appropriate for us to release a UART implementation than rework I2C, but your best option is to switch to SPI.
  • @Matt, thank you for the clarification

    could you tell me where I can find documentation for SPI and how to connect the sensor to the MetaWear RG board?

    so far I've found the SW API under: https://mbientlab.com/androiddocs/latest/spi.html

    but where do get the doc for electrical interface, currently I use "Product Specification v0.8" as the reference but there is nothing about SPI


  • @didlich

    For a SPI bus you need to connect four signals:
    • Slave Select (SS) on MetaWear to the Chip Select (CS) of the target chip
    • Master Out Slave In on MW to the input of the target chip
    • Master In Slave Out on MW to the output of the target chip
    • Clock (SCLK) on MW to the clock input of the target chip

    On the MetaWear side, you can assign any GPIO to any of these functions.  So if you just connect the four target signals to MetaWear, you can assign their functions via the API.

    There are some differences with slaves as to when they expect the clock and data to change.  MetaWear follows the ARM standard definition for "SPI Mode" that is described here: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers .  MetaWear can create any of the timing modes as necessary.

This discussion has been closed.