1

I am trying to write an I2C Communication layer for my PN532 NFC Module.

I have the Arduino code working fine, but I am trying to implement it for Android things. In Arduino my output is as follows:

WriteCommand
Write: 0
Write: 0
Write: 255
Write: 2
Write: 254
Write: 212
Write: 2
Write: 42
Write: 0
/WriteCommand
Read ack frame
Read 1
Read 0
Read 0
Read 255
Read 0
Read 255
Read 0
/Read ack frame

With the following code (copied and modified from a CPP library):

#include <Wire.h>

TwoWire* wire = &Wire;
uint8_t command;

uint8_t pn532_packetbuffer[64];

uint8_t write(uint8_t data) {
    Serial.println("    Write: " + String(data));
    return wire->write(data);
}

uint8_t read() {
    uint8_t value = wire->read();
    Serial.println("    Read " + String(value));
    return value;
}

uint8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) {
    Serial.println("WriteCommand");
    command = header[0];
    wire->beginTransmission(PN532_I2C_ADDRESS);

    write(PN532_PREAMBLE);
    write(PN532_STARTCODE1);
    write(PN532_STARTCODE2);

    uint8_t length = hlen + blen + 1;   // length of data field: TFI + DATA
    write(length);
    write(~length + 1);                 // checksum of length

    write(PN532_HOSTTOPN532);
    uint8_t sum = PN532_HOSTTOPN532;    // sum of TFI + DATA

    for (uint8_t i = 0; i < hlen; i++) {
        if (write(header[i])) {
            sum += header[i];
        } else {
            return PN532_INVALID_FRAME;
        }
    }

    for (uint8_t i = 0; i < blen; i++) {
        if (write(body[i])) {
            sum += body[i];
        } else {
            return PN532_INVALID_FRAME;
        }
    }

    uint8_t checksum = ~sum + 1;            // checksum of TFI + DATA
    write(checksum);
    write(PN532_POSTAMBLE);

    wire->endTransmission();

    Serial.println("/WriteCommand");
    return readAckFrame();
}

uint8_t readAckFrame() {
    Serial.println("Read ack frame");
    const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
    uint8_t ackBuf[sizeof(PN532_ACK)];

    uint16_t time = 0;
    do {
        if (wire->requestFrom(PN532_I2C_ADDRESS,  sizeof(PN532_ACK) + 1)) {
            if (read() & 1) {  // check first byte --- status
                break;         // PN532 is ready
            }
        }

        delay(1);
        time++;
        if (time > PN532_ACK_WAIT_TIME) {
            return PN532_TIMEOUT;
        }
    } while (1);


    for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) {
        ackBuf[i] = read();
    }

    if (memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK))) {
        return PN532_INVALID_ACK;
    }
    Serial.println("/Read ack frame");
    return 0;
}

int16_t getResponseLength(uint8_t buf[], uint8_t len, uint16_t timeout) {
    const uint8_t PN532_NACK[] = {0, 0, 0xFF, 0xFF, 0, 0};
    uint16_t time = 0;

    do {
        if (wire->requestFrom(PN532_I2C_ADDRESS, 6)) {
            if (read() & 1) {  // check first byte --- status
                break;         // PN532 is ready
            }
        }

        delay(1);
        time++;
        if ((0 != timeout) && (time > timeout)) {
            return -1;
        }
    } while (1);

    if (0x00 != read()  ||       // PREAMBLE
        0x00 != read()  ||       // STARTCODE1
        0xFF != read()           // STARTCODE2
    ) {
        return PN532_INVALID_FRAME;
    }

    uint8_t length = read();

    // request for last respond msg again
    wire->beginTransmission(PN532_I2C_ADDRESS);
    for (uint16_t i = 0; i < sizeof(PN532_NACK); ++i) {
        write(PN532_NACK[i]);
    }
    wire->endTransmission();

    return length;
}

int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) {
    uint16_t time = 0;
    uint8_t length;

    length = getResponseLength(buf, len, timeout);

    do {
        if (wire->requestFrom(PN532_I2C_ADDRESS, 6 + length + 2)) {
            if (read() & 1) {  // check first byte --- status
                break;         // PN532 is ready
            }
        }

        delay(1);
        time++;
        if ((0 != timeout) && (time > timeout)) {
            return -1;
        }
    } while (1);

    if (0x00 != read()  ||       // PREAMBLE
        0x00 != read()  ||       // STARTCODE1
        0xFF != read()           // STARTCODE2
    ) {
        return PN532_INVALID_FRAME;
    }

    length = read();

    if (0 != (uint8_t)(length + read())) {   // checksum of length
        return PN532_INVALID_FRAME;
    }

    uint8_t cmd = command + 1;               // response command
    if (PN532_PN532TOHOST != read() || (cmd) != read()) {
        return PN532_INVALID_FRAME;
    }

    length -= 2;
    if (length > len) {
        return PN532_NO_SPACE;  // not enough space
    }

    uint8_t sum = PN532_PN532TOHOST + cmd;
    for (uint8_t i = 0; i < length; i++) {
        buf[i] = read();
        sum += buf[i];
    }

    uint8_t checksum = read();
    if (0 != (uint8_t)(sum + checksum)) {
        return PN532_INVALID_FRAME;
    }
    read();         // POSTAMBLE

    return length;
}

uint32_t getFirmwareVersion(void) {
    uint32_t response;

    pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION;

    if (writeCommand(pn532_packetbuffer, 1, 0, 0)) {
        return 0;
    }

    Serial.println("Wrote the command and got a success acknowledge");
    // read data packet
    int16_t status = readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), 0);
    if (0 > status) {
        return 0;
    }

    response = pn532_packetbuffer[0];
    response <<= 8;
    response |= pn532_packetbuffer[1];
    response <<= 8;
    response |= pn532_packetbuffer[2];
    response <<= 8;
    response |= pn532_packetbuffer[3];

    return response;
}

So in Kotlin I am trying to reproduce this, and while I am writing the correct values I am unable to read the correct values back:

fun Byte.toUnsigned(): Int = when {
    (toInt() < 0) -> 255 + toInt() + 1
    else -> toInt()
}

fun I2cDevice.read(data: ByteArray) {
    read(data, data.size)

    data.forEach {
        android.util.Log.d("I2C","    read: ${it.toUnsigned()}")
    }
}

fun I2cDevice.write(data: ByteArray) {
        write(data, data.size)

    android.util.Log.d("I2C","    write: ${data[0].toUnsigned()}")
}

fun I2cDevice.write(data: Byte) =
    write(ByteArray(1).apply { this[0] = data })

fun I2cDevice.writeCommand(header: ByteArray, body: ByteArray? = null): Int {
    android.util.Log.d("I2C","START")
    write(PN532_PREAMBLE)
    write(PN532_STARTCODE1)
    write(PN532_STARTCODE2)

    val len = (1+header.size + (body?.size?:0)).toByte()
    write(len)
    write((len.inv() + 1).toByte())                 // checksum of length

    write(PN532_HOSTTOPN532)
    var sum: Byte = PN532_HOSTTOPN532    // sum of TFI + DATA

    header.forEach {
        write(it)
        sum = (sum + it).toByte()
    }

    body?.forEach {
        write(it)
        sum = (sum + it).toByte()
    }

    write((sum.inv() + 1).toByte())
    write(PN532_POSTAMBLE)
    return readAckFrame()
}

fun I2cDevice.readAckFrame(): Int {
    val readyCheck = ByteArray(7).apply { this[0] = 0 }

    //while(readyCheck[0].toUnsigned() != 1) {
        read(readyCheck) // <-- will ALWAYS READ 0, 128, 128
    //}
    android.util.Log.d("I2C","Ack complete")
    return 0
}

The problem seems to be that I need to somehow request the data before reading it (wire->requestFrom(PN532_I2C_ADDRESS, sizeof(PN532_ACK) + 1) in the Arduino code). I am writing the correct values and my wiring is correct.

All the Kotlin code is able to read is 0, 128, 128, even though I am asking for 7 bytes.


Fritzing Diagram

  • 1
    Do you have a logic analyzer that you can use to listen to data transmitting on the I2C bus? – Nick Felker Jun 25 '18 at 19:19
  • Never heard of one. I'll see what I can do. Thanks for the tip – Quintin Balsdon Jun 25 '18 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.